Sections

Abstract

In this document, I want to provide a write-up of how this model is similar to and different from the versions previously implemented. This model is meant to provide assembly of multiple sites at the same time, that may, or may not, be connected in some fashion. Along the way, I will mention some of the input and output format that I am expect as documentation. At the end, I will be presenting some preliminary results. I especially want to use this as a vehicle to think about how to analyse those results.

Functions

library(dplyr)     # Data Manipulation
Warning: package ‘dplyr’ was built under R version 4.1.1

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal,
    union
library(ggplot2)   # 2-D Plot
Warning: package ‘ggplot2’ was built under R version 4.1.1
library(plotly)    # 3-D Plot
Warning: package ‘plotly’ was built under R version 4.1.1

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
library(ggfortify) # used for biplots of PCAs
Warning: package ‘ggfortify’ was built under R version 4.1.1
library(vegan)     # Ecological analysis mega-package
Warning: package ‘vegan’ was built under R version 4.1.1
Loading required package: permute
Warning: package ‘permute’ was built under R version 4.1.1
Loading required package: lattice
This is vegan 2.5-7
library(RMTRCode2)

# https://stackoverflow.com/a/7172832
ifrm <- function(obj, env = globalenv()) {
    obj <- deparse(substitute(obj))
    if(exists(obj, envir = env)) {
        rm(list = obj, envir = env)
    }
}

Results, No Spatial Structure

First, we load up the preliminary results. In this case, we are loading a system in which 34 basal species and 66 consumer species form the pool for 10 unconnected environments. The pool and interaction matrix were assembled with the default parameters from Law and Morton’s 1996 work.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-None.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[toRemove] <- 0
ifrm(toRemove)

Events

In total, 9320 events were used in these environments, with the species and environment invasion both randomly assigned. The number of arrival and extinction events were controlled to both be half of this number. We chose this number due to the coupon collecting problem. In particular, we use the result that the probability of encountering each species is bounded: \[\text{Pr}(\text{Draws} < n \log_{e} n + c n) \rightarrow \exp(-\exp(-c)) \text{ as } n \rightarrow \infty\] where \(n\) is the number of species and \(c\) is a constant. For our purposes, we choose \(c = 5\) so that we have a probability of about \(99.3\%\) of seeing each species in each environment. In practice, we failed to observe 2 species-environment combinations. Notably, nearly every species had at least one successful invasion; 5 did not.

The initial abundance was set to be 4000 times the elimination threshold, in line with work on minimum viable populations (Traill et al. 2007). The elimination threshold is admittedly more arbitrary, since it sets an effective individual-area relationship. For this calculation, I set it to \(10^{-4}\), in line with our previous calculations. This is large enough to avoid numerical difficulties from precision, while being low enough to represent a decent sized region.

Since each population is assembling simultaneously, I chose to use exponential waiting times for the inter-species arrival and extinction times. Note that these rates are shared between species and environments, but arrival and extinction are fully independent of each other. Species and environment affected in each event was chosen uniformly at random. The question then is how to set the rate.

To set the rate in this case, I chose to set it to the largest eigenvalue magnitude of the per-environment interaction matrices. This magnitude corresponds to the strongest response we can see from the interaction matrix and, if the interaction matrix is a good approximation for the Jacobian around a stable fixed point (which is not guaranteed), indicates the characteristic time scale of the decay to equilibrium. Hence, (overall) arrival rates and (overall) extinction rates should happen on the same timescale as the (largest) dynamics in the system. Since there are 10 environments, we should then expect that 10 characteristic time scales, on average, should occur in between arrival events in the same environment.

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Every vertical line is a species introduction or extinction by neutral dynamics.

Alpha Diversity plots

Intro

Perhaps more intriguing might be some sense of the biodiversity that we have in each system. We break the abundance results up by environment, then calculate the number of non-zero abundance curves at each time point. We also calculate the Shannon entropy (reminder: higher entropy means more uncertainty which means a flatter distribution).

Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Mean = mean(Richness),
      SpeciesTotal = toString(sort(unique(unlist(strsplit(paste(
        Species, collapse = ", "), split = ", ", fixed = TRUE))))),
      Gamma = unlist(lapply(strsplit(
        SpeciesTotal, split = ", ", fixed = TRUE), length))
    ) %>% tidyr::pivot_longer(
      cols = c(Mean, Gamma), 
      names_to = "Aggregation",
      values_to = "Richness"
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness,
    linetype = Aggregation
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

So richness hovers around a similar regime throughout the majority of the simulation. Note in this plot that we have emphasised one environmental curve and superimposed the mean in black. We do manage to reach heights of 11 species in one environment, but these heights are shortlived. Instead, we seem to observe a (time and environment averaged) value:

mean(Diversity$Richness)
[1] 4.458893

(If we consider the first 10,000 time units as burn-in, we instead see a value of

mean(Diversity$Richness[Diversity$Time > 10000])
[1] 4.587217
Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 8043 row(s) containing missing values (geom_path).
Warning: Removed 2297 row(s) containing missing values (geom_path).

Entropy has a similar, but highly erratic, behaviour. If one tries to follow one of the entropy curves, then one sees that they have fairly substantial periods of almost smooth behaviour followed by suddenly very noisy behaviour, and noise seems to be the dominant mode if one tries to examine the low alpha environment in the background. There are some easy to make predictions. Extinctions reduce the entropy in the system, as you become more certain about what remains. Analogously, arrivals increase the entropy. We can probably better see the relationship beyond these principles by plotting entropy against richness and connecting observations by environment and time.

Entropy-Richness
# ggplot2::ggplot(
#   Diversity %>% dplyr::filter(Environment < 4), 
#   ggplot2::aes(
#     x = Richness,
#     y = Entropy,
#     group = Environment,
#     color = Time
#   )
# ) + ggplot2::geom_path(
# ) + ggplot2::guides(
#   alpha = "none"
# )

plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))

It seems quite hard to tell, but there does not appear to be any particular orientation (clockwise, counter-clockwise) or similar pattern here.

Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 10986 row(s) containing missing values (geom_path).
Warning: Removed 2831 row(s) containing missing values (geom_path).

Evenness helps highlight that this is a high variance process but with a relatively constrained mean.

Environment Diversity

We can, of course, flip the idea on its head. Instead of examining the diversity of species within environments, we can look at the diversity of environments occupied by species. Since very few species end up occupying environments, we just look at richness. Unfortunately, this is quite a memory exhaustive task.

EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

One immediately interesting trend here is that very few species are present across more than 5 environments at a given time. Indeed, only

unique(EnvDiversity$Species[EnvDiversity$Richness > 5])
[1] 12 14 22 30

are ever present in more than 5 environments at once. We can also examine how long these periods occur for by species by tabulation. We block times so that entries are all of the same unit length, and round the average richness during the given time unit.

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7
    1   65970 14384   439     0     0     0     0     0
    2   34776 31378 11352  3287     0     0     0     0
    3   63206 13000  1767  2820     0     0     0     0
    4   46367 21078 11443  1617   288     0     0     0
    5   30648 31553 16542  1341   709     0     0     0
    6   72061  8732     0     0     0     0     0     0
    7   75522  5271     0     0     0     0     0     0
    8   64759 15508   526     0     0     0     0     0
    9   77919  2874     0     0     0     0     0     0
    10  17817 38679 20192  4105     0     0     0     0
    11  52666 24892  3235     0     0     0     0     0
    12   9669  2676  9781 26641 18597  9922  2294  1213
    13  64640 15703   450     0     0     0     0     0
    14  12094 21314 14556 16445 15148   931   305     0
    15  68564 10732  1497     0     0     0     0     0
    16  69690 10903   200     0     0     0     0     0
    17  30636 33714 15505   938     0     0     0     0
    18  69214 11322   257     0     0     0     0     0
    19  62460 17599   734     0     0     0     0     0
    20  74867  3685  1849   392     0     0     0     0
    21  47906 25702  4185  3000     0     0     0     0
    22   3180 13477 20151 21766 16740  5305   174     0
    23  65137 14940   716     0     0     0     0     0
    24  65792 12313  2688     0     0     0     0     0
    25  66085 11109  3599     0     0     0     0     0
    26  41654 19402 19201   536     0     0     0     0
    27  48808 20447 10279  1259     0     0     0     0
    28  68983 11810     0     0     0     0     0     0
    29  34631 26993 17044  2125     0     0     0     0
    30   5561 19065 20682 11364 11597  7963  3301  1260
    31  65019 15665   109     0     0     0     0     0
    32  19327 26015 29600  5851     0     0     0     0
    33  15128 23405 24345 10059  7856     0     0     0
    34  76432  4361     0     0     0     0     0     0
    35  73829  6080   884     0     0     0     0     0
    36  71050  9743     0     0     0     0     0     0
    37  38199 22369 15663  4486    76     0     0     0
    38  28752 35327 16714     0     0     0     0     0
    39  77008  3785     0     0     0     0     0     0
    40  49953 27129  3711     0     0     0     0     0
    41  79838   955     0     0     0     0     0     0
    42  78312  2481     0     0     0     0     0     0
    43  32813 23892 17056  6661   371     0     0     0
    44  39700 23980 15554  1559     0     0     0     0
    45  80793     0     0     0     0     0     0     0
    46  48461 27669  4663     0     0     0     0     0
    47  12053 36783 14826 15182  1949     0     0     0
    48  63151 16980   662     0     0     0     0     0
    49  73125  7668     0     0     0     0     0     0
    50  79860   933     0     0     0     0     0     0
    51  80255   538     0     0     0     0     0     0
    52  73946  6847     0     0     0     0     0     0
    53  77581  3212     0     0     0     0     0     0
    54  76399  4394     0     0     0     0     0     0
    55  55217 23951  1625     0     0     0     0     0
    56  72564  8229     0     0     0     0     0     0
    57  57443 22644   706     0     0     0     0     0
    58  78988  1805     0     0     0     0     0     0
    59  35315 37942  7536     0     0     0     0     0
    60  67881 12912     0     0     0     0     0     0
    61  52226 24749  3818     0     0     0     0     0
    62  62517 16663  1613     0     0     0     0     0
    63  46937 31441  2415     0     0     0     0     0
    64  68974 11213   606     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0
    66  67130 12561  1102     0     0     0     0     0
    67  37887 27036 11834  4036     0     0     0     0
    68  51139 27047  2205   402     0     0     0     0
    69  61791 16492  2510     0     0     0     0     0
    70  73231  7354   208     0     0     0     0     0
    71  22117 22512 20026 14172  1966     0     0     0
    72  10060 26081 33450  7590  3612     0     0     0
    73  73696  7097     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0
    75  77528  3008   257     0     0     0     0     0
    76  34905 34766 10606   516     0     0     0     0
    77  54799 21256  4116   622     0     0     0     0
    78  57557 21260  1976     0     0     0     0     0
    79  46966 28430  5397     0     0     0     0     0
    80  46394 30478  3921     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0
    82  72153  8640     0     0     0     0     0     0
    83  57732 21901  1160     0     0     0     0     0
    84  73061  7732     0     0     0     0     0     0
    85  78108  2685     0     0     0     0     0     0
    86  64841 15952     0     0     0     0     0     0
    87  75894  4488   411     0     0     0     0     0
    88  77088  3705     0     0     0     0     0     0
    89  48328 32465     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0
    91  36647 30720 11100  1868   458     0     0     0
    92  54513 24626  1654     0     0     0     0     0
    93  73946  6847     0     0     0     0     0     0
    94  69274 10406  1113     0     0     0     0     0
    95  43213 20293 14643  2644     0     0     0     0
    96  65015 15778     0     0     0     0     0     0
    97  75428  5365     0     0     0     0     0     0
    98  65968 13004  1821     0     0     0     0     0
    99  78629  2164     0     0     0     0     0     0
    100 52405 15982  9604  2802     0     0     0     0

Beta Diversity

Principal Components Analysis

(For the desktop, the amount of data we generated is too much, so we need to reduce the amount of data we use. To do so we average over time blocks, here of length 100.)

AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)

We can perform a PCA and see if are data can be summarised by a small number of dimensions. As we shall see, constraining to the first 25 principal components does not harm the system much.

PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)

There is not actually a lot of dependence within the system it appears. Consider the amount of variance explained by the first six principal components (ordered, as is tradition, by amount of variation explained).

head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.03749 0.03273 0.03095 0.02751 0.02501 0.02482 

The amount of explained variation is as follows.

sum(summary(PCA)$importance[2, ])
[1] 0.99994

The traditional biplot follows, but despite the seeming presence of patterns, so little of the variance is explained that is probably not worth further examination.

ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

We can try again with presence-absence data instead.

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.05537 0.04584 0.04483 0.03901 0.03578 0.03410 

So it is a bit better, but not by much.

ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

I should note that this is not a failure of the method persay. What this says to me is that dimension reduction of the system cannot reduce the system to a human-readable set of descriptors. The system is still being reduced (from 100 Species * 10 Environments to 25 Components to cover

sum(summary(PCA)$importance[2, ])
[1] 0.99994

of the variation). My initial hypothesis for when the systems are coupled is that, as coupling increases, the number of principal components should decrease, since one system’s changes will be better predictors of the next system’s changes. (An interesting related question; do the number of principal components correlate with the amount of biodiversity in the system?

ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time

Since trying to reduce the system dimensionally does not work (well enough) a next attempt might be to consider how the pair-wise beta diversity changes over the course of the simulation. Initial problems include that there are quite a few measures of beta diversity as well as that the (square of the) number of environments will determine how many entries we need to consider.

To try this, we are going to reorganise our data into data frame with attributes of environment, time, and traits/species.

Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time

One perhaps interesting idea is to try to group the environments by cluster by considering the abundances (or presence-absence) of the species present as traits. The question then is whether the environments have a tendency to attract to specific points or if they wander around each other without relation. (The latter is more neutral.) If they appear to be convergent (which so far would seem to disagree mostly with the alpha diversity analyses), then that would imply that dynamics determine the majority of the system’s fate, while if they instead wander more randomly, then they would appear to be dominated by the neutral mechanisms. (Of course, this is affected by the rate of the neutral mechanisms, so it exists on a definite continuum.) (Note, of course, that cluster analysis usually includes something like PCA, so we would not necessarily expect a different result.)

My working hypothesis for how to use the distance measures is that we need a metric measure (Kindt and Coe, 2005) with any identification scrubbed off (site, time collected). Kindt and Coe suggest that taking the square root of Bray-Curtis makes it metric and thus usable for mathematical distance analyses (such as hierarchical clustering). The vegan package recomends using Jaccard instead of Bray-Curtis, for the same reason.

EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

Note that are major breakdowns extremely early yielding a large number of clusters but there are expected to be 10 environments (if historical contingency mattered most) or a small number of clusters (if they are converging). The large number of clusters says to me that the neutral dynamics are jumping rapidly from cluster to cluster.

We can try again with presence absence instead.

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

Results, Line Spatial Structure

ifrm(Diversity)
ifrm(EnvDistClust)
ifrm(EnvDistClustPA)
ifrm(EnvDiversity)
ifrm(Environments)
ifrm(EnvironmentDistance)
ifrm(EnvironmentDistancePA)

We repeat the procedures above for the linear set. We use the same pool and interaction matrices, but change the space so that ``adjacent’’ communities can disperse (e.g. 1 <-> 2 <-> 3 <->…<-> 9 <-> 10). Similarly, the history is the same in terms of arrivals and extinctions.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt-Result-Env10-Line.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[toRemove] <- 0
ifrm(toRemove)

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Every vertical line is a species introduction or extinction by neutral dynamics. Note that, due to dispersal, these should lose some of their abundance to spreading, but if that spreading is faster than the population can recoup its losses, than it can invade and be wiped out (since invadability does not consider spatial dynamics, only local ecological dynamics). The primary notes then are that there are a large number more events appearing to occur (due to the spatial arrangement), but the system as a whole is stabler (since the species that get knocked out can be recaptured from space as well).

Alpha Diversity plots

Intro
Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Mean = mean(Richness),
      SpeciesTotal = toString(sort(unique(unlist(strsplit(paste(
        Species, collapse = ", "), split = ", ", fixed = TRUE))))),
      Gamma = unlist(lapply(strsplit(
        SpeciesTotal, split = ", ", fixed = TRUE), length))
    ) %>% tidyr::pivot_longer(
      cols = c(Mean, Gamma), 
      names_to = "Aggregation",
      values_to = "Richness"
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness,
    linetype = Aggregation
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1123 row(s) containing missing values (geom_path).
Warning: Removed 110 row(s) containing missing values (geom_path).

Entropy-Richness
plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))
Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1440 row(s) containing missing values (geom_path).
Warning: Removed 136 row(s) containing missing values (geom_path).

Environment Diversity
EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7     8     9    10
    1   80793     0     0     0     0     0     0     0     0     0     0
    2   80624     0     1     1     2     1     1     2     0     2   159
    3   80793     0     0     0     0     0     0     0     0     0     0
    4   80047     0     0     2     0     1     0     0     1     0   742
    5   79905     0     6     0     6     2     5     1     0    11   857
    6   80793     0     0     0     0     0     0     0     0     0     0
    7   80793     0     0     0     0     0     0     0     0     0     0
    8   80793     0     0     0     0     0     0     0     0     0     0
    9   80793     0     0     0     0     0     0     0     0     0     0
    10  80793     0     0     0     0     0     0     0     0     0     0
    11  80793     0     0     0     0     0     0     0     0     0     0
    12    680     0     0     0     0     0     0     0     0     1 80112
    13  80639     1     1     1     1     1     1     2     1     1   144
    14     84     0     0     0     0     0     0     0     0     0 80709
    15  80793     0     0     0     0     0     0     0     0     0     0
    16  80793     0     0     0     0     0     0     0     0     0     0
    17  80793     0     0     0     0     0     0     0     0     0     0
    18  80793     0     0     0     0     0     0     0     0     0     0
    19  80793     0     0     0     0     0     0     0     0     0     0
    20  80793     0     0     0     0     0     0     0     0     0     0
    21  80793     0     0     0     0     0     0     0     0     0     0
    22     62     0     0     0     0     0     0     0     0     1 80730
    23  80793     0     0     0     0     0     0     0     0     0     0
    24  80546     0     1     0     0     0     0     1     0     0   245
    25  80793     0     0     0     0     0     0     0     0     0     0
    26  80793     0     0     0     0     0     0     0     0     0     0
    27  80793     0     0     0     0     0     0     0     0     0     0
    28  80793     0     0     0     0     0     0     0     0     0     0
    29  80721     1     0     0     1     0     0     0     0     1    69
    30  69391     9     2     8     1     8     5     2     8    12 11347
    31  80793     0     0     0     0     0     0     0     0     0     0
    32    500     0     0     0     0     0     1     0     0     1 80291
    33  66491     5    15    10    14     2    14     5    14    17 14206
    34  80793     0     0     0     0     0     0     0     0     0     0
    35  80332     1     0     1     1     1     1     1     0     2   453
    36  80793     0     0     0     0     0     0     0     0     0     0
    37  46279     1     1     2     1     1     3     1     2     4 34498
    38  80793     0     0     0     0     0     0     0     0     0     0
    39  80793     0     0     0     0     0     0     0     0     0     0
    40  80793     0     0     0     0     0     0     0     0     0     0
    41  80793     0     0     0     0     0     0     0     0     0     0
    42  80793     0     0     0     0     0     0     0     0     0     0
    43  27630     2     2     0     4     0     0     1     4     7 53143
    44  60232     1     8     3     3     7     2     7     5    14 20511
    45  80793     0     0     0     0     0     0     0     0     0     0
    46  80793     0     0     0     0     0     0     0     0     0     0
    47  52191     5     8     3     7     3     7     5     9    12 28543
    48  80793     0     0     0     0     0     0     0     0     0     0
    49  80793     0     0     0     0     0     0     0     0     0     0
    50  80793     0     0     0     0     0     0     0     0     0     0
    51  80793     0     0     0     0     0     0     0     0     0     0
    52  80793     0     0     0     0     0     0     0     0     0     0
    53  80793     0     0     0     0     0     0     0     0     0     0
    54  80793     0     0     0     0     0     0     0     0     0     0
    55  78735     1     1     2     0     1     2     0     0     2  2049
    56  80793     0     0     0     0     0     0     0     0     0     0
    57  79020     0     0     1     0     0     0     0     1     0  1771
    58  80793     0     0     0     0     0     0     0     0     0     0
    59  80793     0     0     0     0     0     0     0     0     0     0
    60  80793     0     0     0     0     0     0     0     0     0     0
    61  80466     0     1     0     0     1     1     1     0     1   322
    62  80793     0     0     0     0     0     0     0     0     0     0
    63  80793     0     0     0     0     0     0     0     0     0     0
    64  80793     0     0     0     0     0     0     0     0     0     0
    65  80793     0     0     0     0     0     0     0     0     0     0
    66  80793     0     0     0     0     0     0     0     0     0     0
    67  80793     0     0     0     0     0     0     0     0     0     0
    68  79023     1     5     4     7     5     6     4     4     4  1730
    69  78135     2     2     1     1     1     6     1     2     5  2637
    70  80793     0     0     0     0     0     0     0     0     0     0
    71   1394     0     0     0     0     0     0     0     0     1 79398
    72    292     1     0     0     0     0     1     0     1     0 80498
    73  80793     0     0     0     0     0     0     0     0     0     0
    74  80793     0     0     0     0     0     0     0     0     0     0
    75  80793     0     0     0     0     0     0     0     0     0     0
    76  78676     1     4     1     4     0     6     1     3     6  2091
    77  79765     0     0     1     0     0     1     0     0     1  1025
    78  79253     0     0     1     0     1     0     0     0     0  1538
    79  80793     0     0     0     0     0     0     0     0     0     0
    80  80793     0     0     0     0     0     0     0     0     0     0
    81  80793     0     0     0     0     0     0     0     0     0     0
    82  80793     0     0     0     0     0     0     0     0     0     0
    83  80793     0     0     0     0     0     0     0     0     0     0
    84  80793     0     0     0     0     0     0     0     0     0     0
    85  80793     0     0     0     0     0     0     0     0     0     0
    86  64862     3     6     4     7     4     6     3     8     6 15884
    87  80793     0     0     0     0     0     0     0     0     0     0
    88  80793     0     0     0     0     0     0     0     0     0     0
    89  80793     0     0     0     0     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0     0     0     0
 [ reached getOption("max.print") -- omitted 10 rows ]

Beta Diversity

Principal Components Analysis
AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.19145 0.11089 0.10844 0.07943 0.05993 0.04530 
sum(summary(PCA)$importance[2, ])
[1] 0.99996
ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.21750 0.12185 0.08092 0.05943 0.05215 0.04468 
ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

sum(summary(PCA)$importance[2, ])
[1] 0.99996
ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time
Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time
EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

Results, Slow Line Spatial Structure


ifrm(Diversity)
ifrm(EnvDistClust)
ifrm(EnvDistClustPA)
ifrm(EnvDiversity)
ifrm(Environments)
ifrm(EnvironmentDistance)

We repeat the procedures above for the linear set. We use the same pool and interaction matrices, but change the space so that ``adjacent’’ communities can disperse (e.g. 1 <-> 2 <-> 3 <->…<-> 9 <-> 10). Similarly, the history is the same in terms of arrivals and extinctions.

load(file.path(
  "..", "experiments", "MNA-FirstAttempt1e+06-Result-Env10-Line.RData")
)
toRemove <- result$Abundance <= result$Parameters$EliminationThreshold
result$Abundance[1:(length(toRemove)/2)][toRemove[1:(length(toRemove)/2)]] <- 0
result$Abundance[(length(toRemove)/2 + 1):length(toRemove)][toRemove[(length(toRemove)/2 + 1):length(toRemove)]] <- 0
ifrm(toRemove)

Abundance

With 10 environments, it is probably not helpful to check 10 individual abundance curves, but looking at the first one might be helpful.

LawMorton1996_PlotAbundance(result$Abundance[seq(from = 1, 
                                                 to = nrow(result$Abundance), 
                                                 by = 10), c(1, 2:101)]) -> obj;
obj + ggplot2::scale_y_log10() + ggplot2::guides(color = FALSE)
Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> = "none")` instead.
Warning: Transformation introduced infinite values in continuous y-axis

Alpha Diversity plots

Intro
Diversity <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    richness <- rowSums(env != 0)
    abundSum <- rowSums(env)
    entropy <- env / abundSum
    entropy <- - apply(
      entropy, MARGIN = 1,
      FUN = function(x) {
        sum(ifelse(x != 0, x * log(x), 0))
      })
    species <- apply(
      env, MARGIN = 1,
      FUN = function(x) {
        toString(which(x > 0))
      }
    )
    data.frame(Time = time, 
               Richness = richness, 
               Entropy = entropy,
               Species = species,
               Environment = i)
  },
  abund = result$Abundance,
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)


Diversity <- dplyr::bind_rows(Diversity)
Diversity <- Diversity %>% dplyr::mutate(
  Evenness = Entropy / log(Richness)
)
Richness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Richness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Mean = mean(Richness),
      SpeciesTotal = toString(sort(unique(unlist(strsplit(paste(
        Species, collapse = ", "), split = ", ", fixed = TRUE))))),
      Gamma = unlist(lapply(strsplit(
        SpeciesTotal, split = ", ", fixed = TRUE), length))
    ) %>% tidyr::pivot_longer(
      cols = c(Mean, Gamma), 
      names_to = "Aggregation",
      values_to = "Richness"
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Richness,
    linetype = Aggregation
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)

Entropy
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Entropy,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Entropy = mean(Entropy)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Entropy
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 1855 row(s) containing missing values (geom_path).
Warning: Removed 259 row(s) containing missing values (geom_path).

Entropy-Richness
plotly::plot_ly(data = Diversity %>% dplyr::filter(Environment < 2),
                x = ~Richness, y = ~Entropy, z = ~Time, type = "scatter3d",
                mode = "lines", opacity = 1, line = list(color = ~Time))
Evenness
ggplot2::ggplot(
  Diversity, 
  ggplot2::aes(
    x = Time,
    y = Evenness,
    color = factor(Environment),
    alpha = ifelse(Environment == 1, 1, 0.3)
  )
) + ggplot2::geom_line(
) + ggplot2::geom_line(
  data = Diversity %>% dplyr::group_by(
    Time
    ) %>% dplyr::summarise(
      Evenness = mean(Evenness)
    ),
  mapping = ggplot2::aes(
    x = Time,
    y = Evenness
  ),
  color = "black",
  inherit.aes = FALSE
) + ggplot2::guides(
  alpha = "none"
) + ggplot2::scale_color_discrete(
  "Environment"
)
Warning: Removed 2594 row(s) containing missing values (geom_path).
Warning: Removed 329 row(s) containing missing values (geom_path).

Environment Diversity
EnvDiversity <- lapply(
    1:((ncol(result$Abundance) - 1) / result$NumEnvironments),
    function(i, abund, numSpecies) {
        time <- abund[, 1]
        env <- abund[, 1 + i + numSpecies * (1:result$NumEnvironments - 1)]
        richness <- rowSums(env != 0)
        abundSum <- rowSums(env)
        environments <- apply(
            env, MARGIN = 1,
            FUN = function(x) {
                toString(which(x > 0))
            }
        )
        data.frame(Time = time, 
                   Richness = richness, 
                   Abundance = abundSum,
                   Species = i,
                   Environments = environments)
    },
    abund = result$Abundance,
    numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

EnvDiversity <- dplyr::bind_rows(EnvDiversity)
ggplot2::ggplot(
  EnvDiversity %>% dplyr::filter(Richness > 1), 
  aes(x = Time, y = Richness, color = Species)
) + geom_point(
  alpha = 0.01, size = 3
) + guides(
  color = "none"
)

with(EnvDiversity %>% mutate(
  Time = floor(Time)
  ) %>% group_by(
    Time, Species
    ) %>% summarise(
      Richness = round(mean(Richness)),
      .groups = "drop"
      ),
     table(Species, Richness))
       Richness
Species     0     1     2     3     4     5     6     7     8     9    10
    1   80793     0     0     0     0     0     0     0     0     0     0
    2   73294  1308   345  5846     0     0     0     0     0     0     0
    3   60300 12419   913  7161     0     0     0     0     0     0     0
    4   71970  7391   681   751     0     0     0     0     0     0     0
    5   77639  2117    30  1007     0     0     0     0     0     0     0
    6   80793     0     0     0     0     0     0     0     0     0     0
    7   80793     0     0     0     0     0     0     0     0     0     0
    8   80793     0     0     0     0     0     0     0     0     0     0
    9   80793     0     0     0     0     0     0     0     0     0     0
    10  62872  8776  1697  7448     0     0     0     0     0     0     0
    11  80505   246    42     0     0     0     0     0     0     0     0
    12    680    57     1    67    50    47    85    98   149   131 79428
    13  80793     0     0     0     0     0     0     0     0     0     0
    14     84    17   540   961  2714  3766  9410  3966  6741 12115 40479
    15  71934  2740    85  6034     0     0     0     0     0     0     0
    16  80793     0     0     0     0     0     0     0     0     0     0
    17  47555  7152   340  5033  5534  8193  1609  1541  3836     0     0
    18  80499   294     0     0     0     0     0     0     0     0     0
    19  80686   107     0     0     0     0     0     0     0     0     0
    20  80793     0     0     0     0     0     0     0     0     0     0
    21  79313  1480     0     0     0     0     0     0     0     0     0
    22     62    14     0    32     0    36     0    31     0  2888 77730
    23  80793     0     0     0     0     0     0     0     0     0     0
    24  80793     0     0     0     0     0     0     0     0     0     0
    25  80793     0     0     0     0     0     0     0     0     0     0
    26  80407   386     0     0     0     0     0     0     0     0     0
    27  80793     0     0     0     0     0     0     0     0     0     0
    28  80793     0     0     0     0     0     0     0     0     0     0
    29  79711   114   387   581     0     0     0     0     0     0     0
    30  23183 15781 13353 12433 16043     0     0     0     0     0     0
    31  80793     0     0     0     0     0     0     0     0     0     0
    32   4784 13308 14738  6522  8650 13321  6596  6339  2225  3634   676
    33  34246 12708 12031  8137 10278  2444   722   227     0     0     0
    34  80685   108     0     0     0     0     0     0     0     0     0
    35  80600   193     0     0     0     0     0     0     0     0     0
    36  56841 12740 11212     0     0     0     0     0     0     0     0
    37   7020 40060 26246  7467     0     0     0     0     0     0     0
    38  73593  7200     0     0     0     0     0     0     0     0     0
    39  80793     0     0     0     0     0     0     0     0     0     0
    40  78481  2312     0     0     0     0     0     0     0     0     0
    41  80271   522     0     0     0     0     0     0     0     0     0
    42  73553  7240     0     0     0     0     0     0     0     0     0
    43  18796 19884 26680 11558  3875     0     0     0     0     0     0
    44  30628 14552  5154 12331 12054  6074     0     0     0     0     0
    45  77510  3283     0     0     0     0     0     0     0     0     0
    46  70547  7937   160  2149     0     0     0     0     0     0     0
    47    979  1092 10511 17784 13851 20944 11456  4176     0     0     0
    48  52054 21980  6759     0     0     0     0     0     0     0     0
    49  70981  9812     0     0     0     0     0     0     0     0     0
    50  78655  2138     0     0     0     0     0     0     0     0     0
    51  79858   935     0     0     0     0     0     0     0     0     0
    52  69649  2937   894  5160  2153     0     0     0     0     0     0
    53  79424  1369     0     0     0     0     0     0     0     0     0
    54  78212  1554  1027     0     0     0     0     0     0     0     0
    55  65177 15616     0     0     0     0     0     0     0     0     0
    56  69587 11206     0     0     0     0     0     0     0     0     0
    57  52153 20987  3657  3988     8     0     0     0     0     0     0
    58  71255  9538     0     0     0     0     0     0     0     0     0
    59  56304 24489     0     0     0     0     0     0     0     0     0
    60  74416  6377     0     0     0     0     0     0     0     0     0
    61  67723 12110   960     0     0     0     0     0     0     0     0
    62  66212  4509 10072     0     0     0     0     0     0     0     0
    63  75952  4841     0     0     0     0     0     0     0     0     0
    64  76302  4491     0     0     0     0     0     0     0     0     0
    65  80600   193     0     0     0     0     0     0     0     0     0
    66  76287  4506     0     0     0     0     0     0     0     0     0
    67  51275 22463  7055     0     0     0     0     0     0     0     0
    68  26766 14846  7523 22838  7263  1557     0     0     0     0     0
    69  70929  8209  1600    55     0     0     0     0     0     0     0
    70  75050  5039   704     0     0     0     0     0     0     0     0
    71   7364  4049 25788 11967 18316 13309     0     0     0     0     0
    72    293  6015 31082 15185  8897  9826  6663  2832     0     0     0
    73  65220 15573     0     0     0     0     0     0     0     0     0
    74  76900  3893     0     0     0     0     0     0     0     0     0
    75  78673  2120     0     0     0     0     0     0     0     0     0
    76  60714 19313   662   104     0     0     0     0     0     0     0
    77  66756  3870  5716   138   704   173   375   351  1496  1214     0
    78  68931 11862     0     0     0     0     0     0     0     0     0
    79  74538  6255     0     0     0     0     0     0     0     0     0
    80  70951  9200   642     0     0     0     0     0     0     0     0
    81  79192  1601     0     0     0     0     0     0     0     0     0
    82  80444   349     0     0     0     0     0     0     0     0     0
    83  79662  1131     0     0     0     0     0     0     0     0     0
    84  73288  7505     0     0     0     0     0     0     0     0     0
    85  78008  2785     0     0     0     0     0     0     0     0     0
    86   8225 25572 17279 24066  5651     0     0     0     0     0     0
    87  79538  1255     0     0     0     0     0     0     0     0     0
    88  80538   255     0     0     0     0     0     0     0     0     0
    89  38907 34291  7595     0     0     0     0     0     0     0     0
    90  80793     0     0     0     0     0     0     0     0     0     0
 [ reached getOption("max.print") -- omitted 10 rows ]

Beta Diversity

Principal Components Analysis
AveragedAbundance <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCA <- prcomp(AveragedAbundance %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.06245 0.05166 0.04374 0.03644 0.03204 0.02918 
sum(summary(PCA)$importance[2, ])
[1] 1.00007
ggplot2::autoplot(PCA, loadings = TRUE, 
                  data = AveragedAbundance %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

AveragedPA <- result$Abundance %>% data.frame(
) %>% dplyr::mutate(
  time = floor(time/100)*100
) %>% dplyr::mutate(
  dplyr::across(.cols = !time, .fns = ~ .x > 0)
) %>% dplyr::group_by(
  time
) %>% dplyr::summarise(
  dplyr::across(.fns = ~ mean(.x))
)
PCAPA <- prcomp(AveragedPA %>% dplyr::select_if(~ any(. > 0)), 
              center = TRUE, scale. = TRUE, rank. = 25)
head(summary(PCAPA)$importance[2, ])
    PC1     PC2     PC3     PC4     PC5     PC6 
0.09404 0.07691 0.05775 0.05016 0.04514 0.03782 
ggplot2::autoplot(PCAPA, loadings = TRUE, 
                  data = AveragedPA %>% dplyr::select_if(~ any(. > 0)),
                  colour = "time")

sum(summary(PCA)$importance[2, ])
[1] 1.00007
ifrm(AveragedAbundance)
ifrm(AveragedPA)
ifrm(PCA)
ifrm(PCAPA)
Beta Diversity Over Time
Environments <- lapply(
  1:result$NumEnvironments,
  function(i, abund, numSpecies) {
    time <- abund[, 1]
    env <- abund[, 1 + 1:numSpecies + numSpecies * (i - 1)]
    
    retval <- data.frame(Environment = toString(i),
                         Time = time
    )
    
    retval <- cbind(retval, env)
    colnames(retval) <- c("Environment", "Time", 
                          paste0("Basal", 1:34), 
                          paste0("Consumer", 35:100)
    )
    return(retval)
                          
  },
  abund = result$Abundance %>% data.frame(
  ) %>% dplyr::mutate(
    time = floor(time/100)*100
  ) %>% dplyr::group_by(
    time
  ) %>% dplyr::summarise(
    dplyr::across(.fns = ~ mean(.x))
  ),
  numSpecies = (ncol(result$Abundance) - 1) / result$NumEnvironments
)

Environments <- dplyr::bind_rows(Environments)
Environments <- Environments %>% dplyr::filter(
  dplyr::if_any(dplyr::starts_with(c("Basal", "Consumer")), ~ (.x != 0)),
)
Cluster Analysis Over Time
EnvironmentDistance <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard")
EnvDistClust <- hclust(EnvironmentDistance)
plot(EnvDistClust, labels = FALSE)

EnvironmentDistancePA <- Environments %>% dplyr::select(
  -Environment, -Time
) %>% vegan::vegdist(method = "jaccard", binary = TRUE)
EnvDistClustPA <- hclust(EnvironmentDistancePA)
plot(EnvDistClustPA, labels = FALSE)

LS0tDQp0aXRsZTogIk11bHRpcGxlIE51bWVyaWNhbCBBc3NlbWJseSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCi0tLQ0KDQojIFNlY3Rpb25zIHsudGFic2V0fQ0KDQojIyBBYnN0cmFjdA0KDQpJbiB0aGlzIGRvY3VtZW50LCBJIHdhbnQgdG8gcHJvdmlkZSBhIHdyaXRlLXVwIG9mIGhvdyB0aGlzIG1vZGVsIGlzIHNpbWlsYXIgdG8gYW5kIGRpZmZlcmVudCBmcm9tIHRoZSB2ZXJzaW9ucyBwcmV2aW91c2x5IGltcGxlbWVudGVkLg0KVGhpcyBtb2RlbCBpcyBtZWFudCB0byBwcm92aWRlIGFzc2VtYmx5IG9mIG11bHRpcGxlIHNpdGVzIGF0IHRoZSBzYW1lIHRpbWUsIHRoYXQgbWF5LCBvciBtYXkgbm90LCBiZSBjb25uZWN0ZWQgaW4gc29tZSBmYXNoaW9uLiANCkFsb25nIHRoZSB3YXksIEkgd2lsbCBtZW50aW9uIHNvbWUgb2YgdGhlIGlucHV0IGFuZCBvdXRwdXQgZm9ybWF0IHRoYXQgSSBhbSBleHBlY3QgYXMgZG9jdW1lbnRhdGlvbi4NCkF0IHRoZSBlbmQsIEkgd2lsbCBiZSBwcmVzZW50aW5nIHNvbWUgcHJlbGltaW5hcnkgcmVzdWx0cy4NCkkgZXNwZWNpYWxseSB3YW50IHRvIHVzZSB0aGlzIGFzIGEgdmVoaWNsZSB0byB0aGluayBhYm91dCBob3cgdG8gYW5hbHlzZSB0aG9zZSByZXN1bHRzLg0KDQojIyBGdW5jdGlvbnMNCmBgYHtyfQ0KbGlicmFyeShkcGx5cikgICAgICMgRGF0YSBNYW5pcHVsYXRpb24NCmxpYnJhcnkoZ2dwbG90MikgICAjIDItRCBQbG90DQpsaWJyYXJ5KHBsb3RseSkgICAgIyAzLUQgUGxvdA0KbGlicmFyeShnZ2ZvcnRpZnkpICMgdXNlZCBmb3IgYmlwbG90cyBvZiBQQ0FzDQpsaWJyYXJ5KHZlZ2FuKSAgICAgIyBFY29sb2dpY2FsIGFuYWx5c2lzIG1lZ2EtcGFja2FnZQ0KbGlicmFyeShSTVRSQ29kZTIpDQoNCiMgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9hLzcxNzI4MzINCmlmcm0gPC0gZnVuY3Rpb24ob2JqLCBlbnYgPSBnbG9iYWxlbnYoKSkgew0KICAgIG9iaiA8LSBkZXBhcnNlKHN1YnN0aXR1dGUob2JqKSkNCiAgICBpZihleGlzdHMob2JqLCBlbnZpciA9IGVudikpIHsNCiAgICAgICAgcm0obGlzdCA9IG9iaiwgZW52aXIgPSBlbnYpDQogICAgfQ0KfQ0KYGBgDQoNCg0KIyMgUmVzdWx0cywgTm8gU3BhdGlhbCBTdHJ1Y3R1cmUgey50YWJzZXR9DQoNCkZpcnN0LCB3ZSBsb2FkIHVwIHRoZSBwcmVsaW1pbmFyeSByZXN1bHRzLg0KSW4gdGhpcyBjYXNlLCB3ZSBhcmUgbG9hZGluZyBhIHN5c3RlbSBpbiB3aGljaCANCjM0IGJhc2FsIHNwZWNpZXMgYW5kIDY2IGNvbnN1bWVyIHNwZWNpZXMgZm9ybSB0aGUgcG9vbCBmb3IgMTAgdW5jb25uZWN0ZWQgZW52aXJvbm1lbnRzLg0KVGhlIHBvb2wgYW5kIGludGVyYWN0aW9uIG1hdHJpeCB3ZXJlIGFzc2VtYmxlZCB3aXRoIHRoZSBkZWZhdWx0IHBhcmFtZXRlcnMgZnJvbSBMYXcgYW5kIE1vcnRvbidzIDE5OTYgd29yay4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLU5vbmUuUkRhdGEiKQ0KKQ0KdG9SZW1vdmUgPC0gcmVzdWx0JEFidW5kYW5jZSA8PSByZXN1bHQkUGFyYW1ldGVycyRFbGltaW5hdGlvblRocmVzaG9sZA0KcmVzdWx0JEFidW5kYW5jZVt0b1JlbW92ZV0gPC0gMA0KaWZybSh0b1JlbW92ZSkNCmBgYA0KDQojIyMgRXZlbnRzDQpJbiB0b3RhbCwgYHIgbnJvdyhyZXN1bHQkRXZlbnRzKWAgZXZlbnRzIHdlcmUgdXNlZCBpbiB0aGVzZSBlbnZpcm9ubWVudHMsIHdpdGggdGhlIHNwZWNpZXMgYW5kIGVudmlyb25tZW50IGludmFzaW9uIGJvdGggcmFuZG9tbHkgYXNzaWduZWQuDQpUaGUgbnVtYmVyIG9mIGFycml2YWwgYW5kIGV4dGluY3Rpb24gZXZlbnRzIHdlcmUgY29udHJvbGxlZCB0byBib3RoIGJlIGhhbGYgb2YgdGhpcyBudW1iZXIuDQpXZSBjaG9zZSB0aGlzIG51bWJlciBkdWUgdG8gdGhlIGNvdXBvbiBjb2xsZWN0aW5nIHByb2JsZW0uDQpJbiBwYXJ0aWN1bGFyLCB3ZSB1c2UgdGhlIHJlc3VsdCB0aGF0IFt0aGUgcHJvYmFiaWxpdHkgb2YgZW5jb3VudGVyaW5nIGVhY2ggc3BlY2llcyBpcyBib3VuZGVkOl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ291cG9uX2NvbGxlY3RvciUyN3NfcHJvYmxlbSNFeHRlbnNpb25zX2FuZF9nZW5lcmFsaXphdGlvbnMpDQokJFx0ZXh0e1ByfShcdGV4dHtEcmF3c30gPCBuIFxsb2dfe2V9IG4gKyBjIG4pIFxyaWdodGFycm93IFxleHAoLVxleHAoLWMpKSBcdGV4dHsgYXMgfSBuIFxyaWdodGFycm93IFxpbmZ0eSQkDQp3aGVyZSAkbiQgaXMgdGhlIG51bWJlciBvZiBzcGVjaWVzIGFuZCAkYyQgaXMgYSBjb25zdGFudC4NCkZvciBvdXIgcHVycG9zZXMsIHdlIGNob29zZSAkYyA9IDUkIHNvIHRoYXQgd2UgaGF2ZSBhIHByb2JhYmlsaXR5IG9mIGFib3V0ICQ5OS4zXCUkIG9mIHNlZWluZyBlYWNoIHNwZWNpZXMgaW4gZWFjaCBlbnZpcm9ubWVudC4gDQpJbiBwcmFjdGljZSwgd2UgZmFpbGVkIHRvIG9ic2VydmUgYHIgc3VtKHdpdGgocmVzdWx0JEV2ZW50cywgdGFibGUoU3BlY2llcywgRW52aXJvbm1lbnQpKSA9PSAwKWAgc3BlY2llcy1lbnZpcm9ubWVudCBjb21iaW5hdGlvbnMuDQpOb3RhYmx5LCBuZWFybHkgZXZlcnkgc3BlY2llcyBoYWQgYXQgbGVhc3Qgb25lIHN1Y2Nlc3NmdWwgaW52YXNpb247DQpgciBzdW0ocm93U3Vtcyh3aXRoKHJlc3VsdCRFdmVudHMgJT4lIGZpbHRlcihUeXBlID09ICJBcnJpdmFsIiksIHRhYmxlKFNwZWNpZXMsIEVudmlyb25tZW50LCBTdWNjZXNzKSlbLCAsIDJdKSA9PSAwKWAgZGlkIG5vdC4NCg0KVGhlIGluaXRpYWwgYWJ1bmRhbmNlIHdhcyBzZXQgdG8gYmUgNDAwMCB0aW1lcyB0aGUgZWxpbWluYXRpb24gdGhyZXNob2xkLCBpbiBsaW5lIHdpdGggd29yayBvbiBtaW5pbXVtIHZpYWJsZSBwb3B1bGF0aW9ucyAoVHJhaWxsIGV0IGFsLiAyMDA3KS4NClRoZSBlbGltaW5hdGlvbiB0aHJlc2hvbGQgaXMgYWRtaXR0ZWRseSBtb3JlIGFyYml0cmFyeSwgc2luY2UgaXQgc2V0cyBhbiBlZmZlY3RpdmUgaW5kaXZpZHVhbC1hcmVhIHJlbGF0aW9uc2hpcC4NCkZvciB0aGlzIGNhbGN1bGF0aW9uLCBJIHNldCBpdCB0byAkMTBeey00fSQsIGluIGxpbmUgd2l0aCBvdXIgcHJldmlvdXMgY2FsY3VsYXRpb25zLg0KVGhpcyBpcyBsYXJnZSBlbm91Z2ggdG8gYXZvaWQgbnVtZXJpY2FsIGRpZmZpY3VsdGllcyBmcm9tIHByZWNpc2lvbiwgd2hpbGUgYmVpbmcgbG93IGVub3VnaCB0byByZXByZXNlbnQgYSBkZWNlbnQgc2l6ZWQgcmVnaW9uLg0KDQpTaW5jZSBlYWNoIHBvcHVsYXRpb24gaXMgYXNzZW1ibGluZyBzaW11bHRhbmVvdXNseSwgSSBjaG9zZSB0byB1c2UgZXhwb25lbnRpYWwgd2FpdGluZyB0aW1lcyBmb3IgdGhlIGludGVyLXNwZWNpZXMgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiB0aW1lcy4NCk5vdGUgdGhhdCB0aGVzZSByYXRlcyBhcmUgc2hhcmVkIGJldHdlZW4gc3BlY2llcyBhbmQgZW52aXJvbm1lbnRzLCBidXQgYXJyaXZhbCBhbmQgZXh0aW5jdGlvbiBhcmUgZnVsbHkgaW5kZXBlbmRlbnQgb2YgZWFjaCBvdGhlci4NClNwZWNpZXMgYW5kIGVudmlyb25tZW50IGFmZmVjdGVkIGluIGVhY2ggZXZlbnQgd2FzIGNob3NlbiB1bmlmb3JtbHkgYXQgcmFuZG9tLg0KVGhlIHF1ZXN0aW9uIHRoZW4gaXMgaG93IHRvIHNldCB0aGUgcmF0ZS4NCg0KVG8gc2V0IHRoZSByYXRlIGluIHRoaXMgY2FzZSwgSSBjaG9zZSB0byBzZXQgaXQgdG8gdGhlIGxhcmdlc3QgZWlnZW52YWx1ZSBtYWduaXR1ZGUgb2YgdGhlIHBlci1lbnZpcm9ubWVudCBpbnRlcmFjdGlvbiBtYXRyaWNlcy4NClRoaXMgbWFnbml0dWRlIGNvcnJlc3BvbmRzIHRvIHRoZSBzdHJvbmdlc3QgcmVzcG9uc2Ugd2UgY2FuIHNlZSBmcm9tIHRoZSBpbnRlcmFjdGlvbiBtYXRyaXggYW5kLCBpZiB0aGUgaW50ZXJhY3Rpb24gbWF0cml4IGlzIGEgZ29vZCBhcHByb3hpbWF0aW9uIGZvciB0aGUgSmFjb2JpYW4gYXJvdW5kIGEgc3RhYmxlIGZpeGVkIHBvaW50ICh3aGljaCBpcyBub3QgZ3VhcmFudGVlZCksIGluZGljYXRlcyB0aGUgY2hhcmFjdGVyaXN0aWMgdGltZSBzY2FsZSBvZiB0aGUgZGVjYXkgdG8gZXF1aWxpYnJpdW0uDQpIZW5jZSwgKG92ZXJhbGwpIGFycml2YWwgcmF0ZXMgYW5kIChvdmVyYWxsKSBleHRpbmN0aW9uIHJhdGVzIHNob3VsZCBoYXBwZW4gb24gdGhlIHNhbWUgdGltZXNjYWxlIGFzIHRoZSAobGFyZ2VzdCkgZHluYW1pY3MgaW4gdGhlIHN5c3RlbS4NClNpbmNlIHRoZXJlIGFyZSAxMCBlbnZpcm9ubWVudHMsIHdlIHNob3VsZCB0aGVuIGV4cGVjdCB0aGF0IDEwIGNoYXJhY3RlcmlzdGljIHRpbWUgc2NhbGVzLCBvbiBhdmVyYWdlLCBzaG91bGQgb2NjdXIgaW4gYmV0d2VlbiBhcnJpdmFsIGV2ZW50cyBpbiB0aGUgc2FtZSBlbnZpcm9ubWVudC4NCg0KIyMjIEFidW5kYW5jZSB7LnRhYnNldH0NCg0KV2l0aCAxMCBlbnZpcm9ubWVudHMsIGl0IGlzIHByb2JhYmx5IG5vdCBoZWxwZnVsIHRvIGNoZWNrIDEwIGluZGl2aWR1YWwgYWJ1bmRhbmNlIGN1cnZlcywgYnV0IGxvb2tpbmcgYXQgdGhlIGZpcnN0IG9uZSBtaWdodCBiZSBoZWxwZnVsLg0KYGBge3J9DQpMYXdNb3J0b24xOTk2X1Bsb3RBYnVuZGFuY2UocmVzdWx0JEFidW5kYW5jZVtzZXEoZnJvbSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbnJvdyhyZXN1bHQkQWJ1bmRhbmNlKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAxMCksIGMoMSwgMjoxMDEpXSkgLT4gb2JqOw0KYGBgDQpgYGB7cn0NCm9iaiArIGdncGxvdDI6OnNjYWxlX3lfbG9nMTAoKSArIGdncGxvdDI6Omd1aWRlcyhjb2xvciA9IEZBTFNFKQ0KYGBgDQpFdmVyeSB2ZXJ0aWNhbCBsaW5lIGlzIGEgc3BlY2llcyBpbnRyb2R1Y3Rpb24gb3IgZXh0aW5jdGlvbiBieSBuZXV0cmFsIGR5bmFtaWNzLg0KDQojIyMjIEFscGhhIERpdmVyc2l0eSBwbG90cyB7LnRhYnNldH0NCg0KIyMjIyMgSW50cm8NClBlcmhhcHMgbW9yZSBpbnRyaWd1aW5nIG1pZ2h0IGJlIHNvbWUgc2Vuc2Ugb2YgdGhlIGJpb2RpdmVyc2l0eSB0aGF0IHdlIGhhdmUgaW4gZWFjaCBzeXN0ZW0uDQpXZSBicmVhayB0aGUgYWJ1bmRhbmNlIHJlc3VsdHMgdXAgYnkgZW52aXJvbm1lbnQsIHRoZW4gY2FsY3VsYXRlIHRoZSBudW1iZXIgb2Ygbm9uLXplcm8gYWJ1bmRhbmNlIGN1cnZlcyBhdCBlYWNoIHRpbWUgcG9pbnQuDQpXZSBhbHNvIGNhbGN1bGF0ZSB0aGUgU2hhbm5vbiBlbnRyb3B5IChyZW1pbmRlcjogaGlnaGVyIGVudHJvcHkgbWVhbnMgbW9yZSB1bmNlcnRhaW50eSB3aGljaCBtZWFucyBhIGZsYXR0ZXIgZGlzdHJpYnV0aW9uKS4NCmBgYHtyfQ0KRGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgIGVudHJvcHkgPC0gZW52IC8gYWJ1bmRTdW0NCiAgICBlbnRyb3B5IDwtIC0gYXBwbHkoDQogICAgICBlbnRyb3B5LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oaWZlbHNlKHggIT0gMCwgeCAqIGxvZyh4KSwgMCkpDQogICAgICB9KQ0KICAgIHNwZWNpZXMgPC0gYXBwbHkoDQogICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgIH0NCiAgICApDQogICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgIEVudHJvcHkgPSBlbnRyb3B5LA0KICAgICAgICAgICAgICAgU3BlY2llcyA9IHNwZWNpZXMsDQogICAgICAgICAgICAgICBFbnZpcm9ubWVudCA9IGkpDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCg0KRGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRGl2ZXJzaXR5KQ0KRGl2ZXJzaXR5IDwtIERpdmVyc2l0eSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgRXZlbm5lc3MgPSBFbnRyb3B5IC8gbG9nKFJpY2huZXNzKQ0KKQ0KYGBgDQoNCg0KIyMjIyMgUmljaG5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBNZWFuID0gbWVhbihSaWNobmVzcyksDQogICAgICBTcGVjaWVzVG90YWwgPSB0b1N0cmluZyhzb3J0KHVuaXF1ZSh1bmxpc3Qoc3Ryc3BsaXQocGFzdGUoDQogICAgICAgIFNwZWNpZXMsIGNvbGxhcHNlID0gIiwgIiksIHNwbGl0ID0gIiwgIiwgZml4ZWQgPSBUUlVFKSkpKSksDQogICAgICBHYW1tYSA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoDQogICAgICAgIFNwZWNpZXNUb3RhbCwgc3BsaXQgPSAiLCAiLCBmaXhlZCA9IFRSVUUpLCBsZW5ndGgpKQ0KICAgICkgJT4lIHRpZHlyOjpwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gYyhNZWFuLCBHYW1tYSksIA0KICAgICAgbmFtZXNfdG8gPSAiQWdncmVnYXRpb24iLA0KICAgICAgdmFsdWVzX3RvID0gIlJpY2huZXNzIg0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGxpbmV0eXBlID0gQWdncmVnYXRpb24NCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANClNvIHJpY2huZXNzIGhvdmVycyBhcm91bmQgYSBzaW1pbGFyIHJlZ2ltZSB0aHJvdWdob3V0IHRoZSBtYWpvcml0eSBvZiB0aGUgc2ltdWxhdGlvbi4NCk5vdGUgaW4gdGhpcyBwbG90IHRoYXQgd2UgaGF2ZSBlbXBoYXNpc2VkIG9uZSBlbnZpcm9ubWVudGFsIGN1cnZlIGFuZCBzdXBlcmltcG9zZWQgdGhlIG1lYW4gaW4gYmxhY2suDQpXZSBkbyBtYW5hZ2UgdG8gcmVhY2ggaGVpZ2h0cyBvZiAxMSBzcGVjaWVzIGluIG9uZSBlbnZpcm9ubWVudCwgYnV0IHRoZXNlIGhlaWdodHMgYXJlIHNob3J0bGl2ZWQuDQpJbnN0ZWFkLCB3ZSBzZWVtIHRvIG9ic2VydmUgYSAodGltZSBhbmQgZW52aXJvbm1lbnQgYXZlcmFnZWQpIHZhbHVlOg0KYGBge3J9IA0KbWVhbihEaXZlcnNpdHkkUmljaG5lc3MpDQpgYGANCihJZiB3ZSBjb25zaWRlciB0aGUgZmlyc3QgMTAsMDAwIHRpbWUgdW5pdHMgYXMgYnVybi1pbiwgd2UgaW5zdGVhZCBzZWUgYSB2YWx1ZSBvZg0KYGBge3J9IA0KbWVhbihEaXZlcnNpdHkkUmljaG5lc3NbRGl2ZXJzaXR5JFRpbWUgPiAxMDAwMF0pDQpgYGANCg0KDQojIyMjIyBFbnRyb3B5DQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5LA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFbnRyb3B5ID0gbWVhbihFbnRyb3B5KQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHkNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkVudHJvcHkgaGFzIGEgc2ltaWxhciwgYnV0IGhpZ2hseSBlcnJhdGljLCBiZWhhdmlvdXIuDQpJZiBvbmUgdHJpZXMgdG8gZm9sbG93IG9uZSBvZiB0aGUgZW50cm9weSBjdXJ2ZXMsIHRoZW4gb25lIHNlZXMgdGhhdCB0aGV5IGhhdmUNCmZhaXJseSBzdWJzdGFudGlhbCBwZXJpb2RzIG9mIGFsbW9zdCBzbW9vdGggYmVoYXZpb3VyIGZvbGxvd2VkIGJ5IHN1ZGRlbmx5DQp2ZXJ5IG5vaXN5IGJlaGF2aW91ciwgYW5kIG5vaXNlIHNlZW1zIHRvIGJlIHRoZSBkb21pbmFudCBtb2RlIGlmIG9uZSB0cmllcw0KdG8gZXhhbWluZSB0aGUgbG93IGFscGhhIGVudmlyb25tZW50IGluIHRoZSBiYWNrZ3JvdW5kLg0KVGhlcmUgYXJlIHNvbWUgZWFzeSB0byBtYWtlIHByZWRpY3Rpb25zLg0KRXh0aW5jdGlvbnMgcmVkdWNlIHRoZSBlbnRyb3B5IGluIHRoZSBzeXN0ZW0sIGFzIHlvdSBiZWNvbWUgbW9yZSBjZXJ0YWluIGFib3V0DQp3aGF0IHJlbWFpbnMuDQpBbmFsb2dvdXNseSwgYXJyaXZhbHMgaW5jcmVhc2UgdGhlIGVudHJvcHkuDQpXZSBjYW4gcHJvYmFibHkgYmV0dGVyIHNlZSB0aGUgcmVsYXRpb25zaGlwIGJleW9uZCB0aGVzZSBwcmluY2lwbGVzIGJ5IHBsb3R0aW5nDQplbnRyb3B5IGFnYWluc3QgcmljaG5lc3MgYW5kIGNvbm5lY3Rpbmcgb2JzZXJ2YXRpb25zIGJ5IGVudmlyb25tZW50IGFuZCB0aW1lLg0KDQojIyMjIyBFbnRyb3B5LVJpY2huZXNzDQpgYGB7cn0NCiMgZ2dwbG90Mjo6Z2dwbG90KA0KIyAgIERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDQpLCANCiMgICBnZ3Bsb3QyOjphZXMoDQojICAgICB4ID0gUmljaG5lc3MsDQojICAgICB5ID0gRW50cm9weSwNCiMgICAgIGdyb3VwID0gRW52aXJvbm1lbnQsDQojICAgICBjb2xvciA9IFRpbWUNCiMgICApDQojICkgKyBnZ3Bsb3QyOjpnZW9tX3BhdGgoDQojICkgKyBnZ3Bsb3QyOjpndWlkZXMoDQojICAgYWxwaGEgPSAibm9uZSINCiMgKQ0KDQpwbG90bHk6OnBsb3RfbHkoZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDIpLA0KICAgICAgICAgICAgICAgIHggPSB+UmljaG5lc3MsIHkgPSB+RW50cm9weSwgeiA9IH5UaW1lLCB0eXBlID0gInNjYXR0ZXIzZCIsDQogICAgICAgICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIG9wYWNpdHkgPSAxLCBsaW5lID0gbGlzdChjb2xvciA9IH5UaW1lKSkNCmBgYA0KSXQgc2VlbXMgcXVpdGUgaGFyZCB0byB0ZWxsLCBidXQgdGhlcmUgZG9lcyBub3QgYXBwZWFyIHRvIGJlIGFueSBwYXJ0aWN1bGFyIG9yaWVudGF0aW9uIChjbG9ja3dpc2UsIGNvdW50ZXItY2xvY2t3aXNlKSBvciBzaW1pbGFyIHBhdHRlcm4gaGVyZS4NCg0KIyMjIyMgRXZlbm5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFdmVubmVzcyA9IG1lYW4oRXZlbm5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCkV2ZW5uZXNzIGhlbHBzIGhpZ2hsaWdodCB0aGF0IHRoaXMgaXMgYSBoaWdoIHZhcmlhbmNlIHByb2Nlc3MgYnV0IHdpdGggYSByZWxhdGl2ZWx5IGNvbnN0cmFpbmVkIG1lYW4uDQoNCiMjIyMjIEVudmlyb25tZW50IERpdmVyc2l0eQ0KV2UgY2FuLCBvZiBjb3Vyc2UsIGZsaXAgdGhlIGlkZWEgb24gaXRzIGhlYWQuDQpJbnN0ZWFkIG9mIGV4YW1pbmluZyB0aGUgZGl2ZXJzaXR5IG9mIHNwZWNpZXMgd2l0aGluIGVudmlyb25tZW50cywgd2UgY2FuDQpsb29rIGF0IHRoZSBkaXZlcnNpdHkgb2YgZW52aXJvbm1lbnRzIG9jY3VwaWVkIGJ5IHNwZWNpZXMuDQpTaW5jZSB2ZXJ5IGZldyBzcGVjaWVzIGVuZCB1cCBvY2N1cHlpbmcgZW52aXJvbm1lbnRzLCB3ZSBqdXN0IGxvb2sgYXQgcmljaG5lc3MuDQpVbmZvcnR1bmF0ZWx5LCB0aGlzIGlzIHF1aXRlIGEgbWVtb3J5IGV4aGF1c3RpdmUgdGFzay4NCg0KYGBge3J9DQpFbnZEaXZlcnNpdHkgPC0gbGFwcGx5KA0KICAgIDE6KChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzKSwNCiAgICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgICAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICAgICAgZW52IDwtIGFidW5kWywgMSArIGkgKyBudW1TcGVjaWVzICogKDE6cmVzdWx0JE51bUVudmlyb25tZW50cyAtIDEpXQ0KICAgICAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgICAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICAgICAgZW52aXJvbm1lbnRzIDwtIGFwcGx5KA0KICAgICAgICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgKQ0KICAgICAgICBkYXRhLmZyYW1lKFRpbWUgPSB0aW1lLCANCiAgICAgICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgICAgICBBYnVuZGFuY2UgPSBhYnVuZFN1bSwNCiAgICAgICAgICAgICAgICAgICBTcGVjaWVzID0gaSwNCiAgICAgICAgICAgICAgICAgICBFbnZpcm9ubWVudHMgPSBlbnZpcm9ubWVudHMpDQogICAgfSwNCiAgICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogICAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKEVudkRpdmVyc2l0eSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRW52RGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKFJpY2huZXNzID4gMSksIA0KICBhZXMoeCA9IFRpbWUsIHkgPSBSaWNobmVzcywgY29sb3IgPSBTcGVjaWVzKQ0KKSArIGdlb21fcG9pbnQoDQogIGFscGhhID0gMC4wMSwgc2l6ZSA9IDMNCikgKyBndWlkZXMoDQogIGNvbG9yID0gIm5vbmUiDQopDQpgYGANCg0KT25lIGltbWVkaWF0ZWx5IGludGVyZXN0aW5nIHRyZW5kIGhlcmUgaXMgdGhhdCB2ZXJ5IGZldyBzcGVjaWVzIGFyZSBwcmVzZW50DQphY3Jvc3MgbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IGEgZ2l2ZW4gdGltZS4NCkluZGVlZCwgb25seSANCmBgYHtyfSANCnVuaXF1ZShFbnZEaXZlcnNpdHkkU3BlY2llc1tFbnZEaXZlcnNpdHkkUmljaG5lc3MgPiA1XSkNCmBgYCANCmFyZSBldmVyIHByZXNlbnQgaW4gbW9yZSB0aGFuIDUgZW52aXJvbm1lbnRzIGF0IG9uY2UuDQpXZSBjYW4gYWxzbyBleGFtaW5lIGhvdyBsb25nIHRoZXNlIHBlcmlvZHMgb2NjdXIgZm9yIGJ5IHNwZWNpZXMgYnkgdGFidWxhdGlvbi4NCldlIGJsb2NrIHRpbWVzIHNvIHRoYXQgZW50cmllcyBhcmUgYWxsIG9mIHRoZSBzYW1lIHVuaXQgbGVuZ3RoLCBhbmQgcm91bmQgdGhlDQphdmVyYWdlIHJpY2huZXNzIGR1cmluZyB0aGUgZ2l2ZW4gdGltZSB1bml0Lg0KDQpgYGB7cn0NCndpdGgoRW52RGl2ZXJzaXR5ICU+JSBtdXRhdGUoDQogIFRpbWUgPSBmbG9vcihUaW1lKQ0KICApICU+JSBncm91cF9ieSgNCiAgICBUaW1lLCBTcGVjaWVzDQogICAgKSAlPiUgc3VtbWFyaXNlKA0KICAgICAgUmljaG5lc3MgPSByb3VuZChtZWFuKFJpY2huZXNzKSksDQogICAgICAuZ3JvdXBzID0gImRyb3AiDQogICAgICApLA0KICAgICB0YWJsZShTcGVjaWVzLCBSaWNobmVzcykpDQpgYGANCiMjIyMgQmV0YSBEaXZlcnNpdHkgey50YWJzZXR9DQoNCiMjIyMjIFByaW5jaXBhbCBDb21wb25lbnRzIEFuYWx5c2lzDQooRm9yIHRoZSBkZXNrdG9wLCB0aGUgYW1vdW50IG9mIGRhdGEgd2UgZ2VuZXJhdGVkIGlzIHRvbyBtdWNoLCBzbyB3ZSBuZWVkIHRvDQpyZWR1Y2UgdGhlIGFtb3VudCBvZiBkYXRhIHdlIHVzZS4NClRvIGRvIHNvIHdlIGF2ZXJhZ2Ugb3ZlciB0aW1lIGJsb2NrcywgaGVyZSBvZiBsZW5ndGggMTAwLikNCmBgYHtyfQ0KQXZlcmFnZWRBYnVuZGFuY2UgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KV2UgY2FuIHBlcmZvcm0gYSBQQ0EgYW5kIHNlZSBpZiBhcmUgZGF0YSBjYW4gYmUgc3VtbWFyaXNlZCBieSBhIHNtYWxsIG51bWJlciBvZg0KZGltZW5zaW9ucy4NCkFzIHdlIHNoYWxsIHNlZSwgY29uc3RyYWluaW5nIHRvIHRoZSBmaXJzdCAyNSBwcmluY2lwYWwgY29tcG9uZW50cyBkb2VzIG5vdA0KaGFybSB0aGUgc3lzdGVtIG11Y2guDQpgYGB7cn0NClBDQSA8LSBwcmNvbXAoQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KVGhlcmUgaXMgbm90IGFjdHVhbGx5IGEgbG90IG9mIGRlcGVuZGVuY2Ugd2l0aGluIHRoZSBzeXN0ZW0gaXQgYXBwZWFycy4NCkNvbnNpZGVyIHRoZSBhbW91bnQgb2YgdmFyaWFuY2UgZXhwbGFpbmVkIGJ5IHRoZSBmaXJzdCBzaXggcHJpbmNpcGFsIGNvbXBvbmVudHMNCihvcmRlcmVkLCBhcyBpcyB0cmFkaXRpb24sIGJ5IGFtb3VudCBvZiB2YXJpYXRpb24gZXhwbGFpbmVkKS4NCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KVGhlIGFtb3VudCBvZiBleHBsYWluZWQgdmFyaWF0aW9uIGlzIGFzIGZvbGxvd3MuDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNClRoZSB0cmFkaXRpb25hbCBiaXBsb3QgZm9sbG93cywgYnV0IGRlc3BpdGUgdGhlIHNlZW1pbmcgcHJlc2VuY2Ugb2YgcGF0dGVybnMsDQpzbyBsaXR0bGUgb2YgdGhlIHZhcmlhbmNlIGlzIGV4cGxhaW5lZCB0aGF0IGlzIHByb2JhYmx5IG5vdCB3b3J0aCBmdXJ0aGVyDQpleGFtaW5hdGlvbi4NCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0EsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpXZSBjYW4gdHJ5IGFnYWluIHdpdGggcHJlc2VuY2UtYWJzZW5jZSBkYXRhIGluc3RlYWQuDQpgYGB7cn0NCkF2ZXJhZ2VkUEEgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBkcGx5cjo6YWNyb3NzKC5jb2xzID0gIXRpbWUsIC5mbnMgPSB+IC54ID4gMCkNCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgdGltZQ0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCikNCmBgYA0KDQpgYGB7cn0NClBDQVBBIDwtIHByY29tcChBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQVBBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANClNvIGl0IGlzIGEgYml0IGJldHRlciwgYnV0IG5vdCBieSBtdWNoLg0KDQpgYGB7cn0NCmdncGxvdDI6OmF1dG9wbG90KFBDQVBBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkUEEgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpJIHNob3VsZCBub3RlIHRoYXQgdGhpcyBpcyBub3QgYSBmYWlsdXJlIG9mIHRoZSBtZXRob2QgcGVyc2F5LiANCldoYXQgdGhpcyBzYXlzIHRvIG1lIGlzIHRoYXQgZGltZW5zaW9uIHJlZHVjdGlvbiBvZiB0aGUgc3lzdGVtIGNhbm5vdCByZWR1Y2UgdGhlDQpzeXN0ZW0gdG8gYSBodW1hbi1yZWFkYWJsZSBzZXQgb2YgZGVzY3JpcHRvcnMuDQpUaGUgc3lzdGVtIGlzIHN0aWxsIGJlaW5nIHJlZHVjZWQgKGZyb20gMTAwIFNwZWNpZXMgKiAxMCBFbnZpcm9ubWVudHMgdG8gMjUgQ29tcG9uZW50cyB0byBjb3ZlciANCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgIA0Kb2YgdGhlIHZhcmlhdGlvbikuDQo8IS0tIA0KSW5zdGVhZCwgZ2l2ZW4gdGhlIG51bWJlciBvZiBlbnZpcm9ubWVudHMgYW5kIHRoZWlyIGluZGVwZW5kZW5jZSwgdGhpcyBzZWVtcyB0byANCnN1Z2dlc3QgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdGhlIGNoYW5nZSBpbiBlbnZpcm9ubWVudHMgY2FuIGJlIGRlc2NyaWJlZCBieSBhYm91dCB0d28gY29tcG9uZW50cyBwbHVzIHRpbWUgYW5kIHNvbWUgbWlub3IgdmFyaWF0aW9uIHdoZW4gY29uc2lkZXJpbmcgdGhlIHN5c3RlbSBhcyBhIHdob2xlLiANCihUaGlzIGRvZXMgbm90IHdvcmsgZm9yIHRoZSBpbmRpdmlkdWFsIHN5c3RlbXMgc2luY2UgdGhlcmUgaXMgcmVkdW5kYW5jeSB0aGF0IGlzIGxvc3QuKSAtLT4NCk15IGluaXRpYWwgaHlwb3RoZXNpcyBmb3Igd2hlbiB0aGUgc3lzdGVtcyBhcmUgY291cGxlZCBpcyB0aGF0LCBhcyBjb3VwbGluZyBpbmNyZWFzZXMsIHRoZSBudW1iZXIgb2YgcHJpbmNpcGFsIGNvbXBvbmVudHMgc2hvdWxkIGRlY3JlYXNlLCBzaW5jZSBvbmUgc3lzdGVtJ3MgY2hhbmdlcyB3aWxsIGJlIGJldHRlciBwcmVkaWN0b3JzIG9mIHRoZSBuZXh0IHN5c3RlbSdzIGNoYW5nZXMuDQooQW4gaW50ZXJlc3RpbmcgcmVsYXRlZCBxdWVzdGlvbjsgZG8gdGhlIG51bWJlciBvZiBwcmluY2lwYWwgY29tcG9uZW50cyBjb3JyZWxhdGUgd2l0aCB0aGUgYW1vdW50IG9mIGJpb2RpdmVyc2l0eSBpbiB0aGUgc3lzdGVtPw0KDQpgYGB7ciBjbGVhbnVwfQ0KaWZybShBdmVyYWdlZEFidW5kYW5jZSkNCmlmcm0oQXZlcmFnZWRQQSkNCmlmcm0oUENBKQ0KaWZybShQQ0FQQSkNCmBgYA0KDQojIyMjIyBCZXRhIERpdmVyc2l0eSBPdmVyIFRpbWUNClNpbmNlIHRyeWluZyB0byByZWR1Y2UgdGhlIHN5c3RlbSBkaW1lbnNpb25hbGx5IGRvZXMgbm90IHdvcmsgKHdlbGwgZW5vdWdoKSBhDQpuZXh0IGF0dGVtcHQgbWlnaHQgYmUgdG8gY29uc2lkZXIgaG93IHRoZSBwYWlyLXdpc2UgYmV0YSBkaXZlcnNpdHkgY2hhbmdlcyBvdmVyDQp0aGUgY291cnNlIG9mIHRoZSBzaW11bGF0aW9uLg0KSW5pdGlhbCBwcm9ibGVtcyBpbmNsdWRlIHRoYXQgdGhlcmUgYXJlIHF1aXRlIGEgZmV3IG1lYXN1cmVzIG9mIGJldGEgZGl2ZXJzaXR5IA0KYXMgd2VsbCBhcyB0aGF0IHRoZSAoc3F1YXJlIG9mIHRoZSkgbnVtYmVyIG9mIGVudmlyb25tZW50cyB3aWxsIGRldGVybWluZSBob3cNCm1hbnkgZW50cmllcyB3ZSBuZWVkIHRvIGNvbnNpZGVyLg0KDQpUbyB0cnkgdGhpcywgd2UgYXJlIGdvaW5nIHRvIHJlb3JnYW5pc2Ugb3VyIGRhdGEgaW50byBkYXRhIGZyYW1lIHdpdGggYXR0cmlidXRlcw0Kb2YgZW52aXJvbm1lbnQsIHRpbWUsIGFuZCB0cmFpdHMvc3BlY2llcy4NCmBgYHtyfQ0KRW52aXJvbm1lbnRzIDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICANCiAgICByZXR2YWwgPC0gZGF0YS5mcmFtZShFbnZpcm9ubWVudCA9IHRvU3RyaW5nKGkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSB0aW1lDQogICAgKQ0KICAgIA0KICAgIHJldHZhbCA8LSBjYmluZChyZXR2YWwsIGVudikNCiAgICBjb2xuYW1lcyhyZXR2YWwpIDwtIGMoIkVudmlyb25tZW50IiwgIlRpbWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCYXNhbCIsIDE6MzQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJDb25zdW1lciIsIDM1OjEwMCkNCiAgICApDQogICAgcmV0dXJuKHJldHZhbCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KICApICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgdGltZQ0KICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQogICksDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZpcm9ubWVudHMgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZpcm9ubWVudHMpDQpFbnZpcm9ubWVudHMgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICBkcGx5cjo6aWZfYW55KGRwbHlyOjpzdGFydHNfd2l0aChjKCJCYXNhbCIsICJDb25zdW1lciIpKSwgfiAoLnggIT0gMCkpLA0KKQ0KYGBgDQoNCiMjIyMjIENsdXN0ZXIgQW5hbHlzaXMgT3ZlciBUaW1lDQpPbmUgcGVyaGFwcyBpbnRlcmVzdGluZyBpZGVhIGlzIHRvIHRyeSB0byBncm91cCB0aGUgZW52aXJvbm1lbnRzIGJ5IGNsdXN0ZXINCmJ5IGNvbnNpZGVyaW5nIHRoZSBhYnVuZGFuY2VzIChvciBwcmVzZW5jZS1hYnNlbmNlKSBvZiB0aGUgc3BlY2llcyBwcmVzZW50IGFzIHRyYWl0cy4NClRoZSBxdWVzdGlvbiB0aGVuIGlzIHdoZXRoZXIgdGhlIGVudmlyb25tZW50cyBoYXZlIGEgdGVuZGVuY3kgdG8gYXR0cmFjdCB0byANCnNwZWNpZmljIHBvaW50cyBvciBpZiB0aGV5IHdhbmRlciBhcm91bmQgZWFjaCBvdGhlciB3aXRob3V0IHJlbGF0aW9uLg0KKFRoZSBsYXR0ZXIgaXMgbW9yZSBuZXV0cmFsLikNCklmIHRoZXkgYXBwZWFyIHRvIGJlIGNvbnZlcmdlbnQgKHdoaWNoIHNvIGZhciB3b3VsZCBzZWVtIHRvIGRpc2FncmVlIG1vc3RseSB3aXRoDQp0aGUgYWxwaGEgZGl2ZXJzaXR5IGFuYWx5c2VzKSwgdGhlbiB0aGF0IHdvdWxkIGltcGx5IHRoYXQgZHluYW1pY3MgZGV0ZXJtaW5lIHRoZQ0KbWFqb3JpdHkgb2YgdGhlIHN5c3RlbSdzIGZhdGUsIHdoaWxlIGlmIHRoZXkgaW5zdGVhZCB3YW5kZXIgbW9yZSByYW5kb21seSwgdGhlbg0KdGhleSB3b3VsZCBhcHBlYXIgdG8gYmUgZG9taW5hdGVkIGJ5IHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMuDQooT2YgY291cnNlLCB0aGlzIGlzIGFmZmVjdGVkIGJ5IHRoZSByYXRlIG9mIHRoZSBuZXV0cmFsIG1lY2hhbmlzbXMsIHNvIGl0IGV4aXN0cw0Kb24gYSBkZWZpbml0ZSBjb250aW51dW0uKQ0KKE5vdGUsIG9mIGNvdXJzZSwgdGhhdCBjbHVzdGVyIGFuYWx5c2lzIHVzdWFsbHkgaW5jbHVkZXMgc29tZXRoaW5nIGxpa2UgUENBLA0Kc28gd2Ugd291bGQgbm90IG5lY2Vzc2FyaWx5IGV4cGVjdCBhIGRpZmZlcmVudCByZXN1bHQuKQ0KDQpNeSB3b3JraW5nIGh5cG90aGVzaXMgZm9yIGhvdyB0byB1c2UgdGhlIGRpc3RhbmNlIG1lYXN1cmVzIGlzIHRoYXQgd2UgbmVlZCBhDQptZXRyaWMgbWVhc3VyZSAoS2luZHQgYW5kIENvZSwgMjAwNSkgd2l0aCBhbnkgaWRlbnRpZmljYXRpb24gc2NydWJiZWQgb2ZmIChzaXRlLA0KdGltZSBjb2xsZWN0ZWQpLg0KS2luZHQgYW5kIENvZSBzdWdnZXN0IHRoYXQgdGFraW5nIHRoZSBzcXVhcmUgcm9vdCBvZiBCcmF5LUN1cnRpcyBtYWtlcyBpdCBtZXRyaWMNCmFuZCB0aHVzIHVzYWJsZSBmb3IgbWF0aGVtYXRpY2FsIGRpc3RhbmNlIGFuYWx5c2VzIChzdWNoIGFzIGhpZXJhcmNoaWNhbCANCmNsdXN0ZXJpbmcpLg0KVGhlIGB2ZWdhbmAgcGFja2FnZSByZWNvbWVuZHMgdXNpbmcgSmFjY2FyZCBpbnN0ZWFkIG9mIEJyYXktQ3VydGlzLCBmb3IgdGhlIHNhbWUNCnJlYXNvbi4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OnNlbGVjdCgNCiAgLUVudmlyb25tZW50LCAtVGltZQ0KKSAlPiUgdmVnYW46OnZlZ2Rpc3QobWV0aG9kID0gImphY2NhcmQiKQ0KYGBgDQoNCmBgYHtyfQ0KRW52RGlzdENsdXN0IDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3QsIGxhYmVscyA9IEZBTFNFKQ0KYGBgDQpOb3RlIHRoYXQgYXJlIG1ham9yIGJyZWFrZG93bnMgZXh0cmVtZWx5IGVhcmx5IHlpZWxkaW5nIGEgbGFyZ2UgbnVtYmVyIG9mIA0KY2x1c3RlcnMgYnV0IHRoZXJlIGFyZSBleHBlY3RlZCB0byBiZSAxMCBlbnZpcm9ubWVudHMgKGlmIGhpc3RvcmljYWwgY29udGluZ2VuY3kgDQptYXR0ZXJlZCBtb3N0KSBvciBhIHNtYWxsIG51bWJlciBvZiBjbHVzdGVycyAoaWYgdGhleSBhcmUgY29udmVyZ2luZykuDQpUaGUgbGFyZ2UgbnVtYmVyIG9mIGNsdXN0ZXJzIHNheXMgdG8gbWUgdGhhdCB0aGUgbmV1dHJhbCBkeW5hbWljcyBhcmUganVtcGluZw0KcmFwaWRseSBmcm9tIGNsdXN0ZXIgdG8gY2x1c3Rlci4NCg0KV2UgY2FuIHRyeSBhZ2FpbiB3aXRoIHByZXNlbmNlIGFic2VuY2UgaW5zdGVhZC4NCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlUEEgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIsIGJpbmFyeSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3RQQSA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZVBBKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3RQQSwgbGFiZWxzID0gRkFMU0UpDQpgYGANCg0KPCEtLSMjIyMjIFN0YXRlIE5ldHdvcmsNCkFuIGFkZGl0aW9uYWwgaWRlYSB0aGF0IG1pZ2h0IGJlIHdvcnRoIGFuIGluaXRpYWwgbG9vayBvdmVyIGlzIHdoZXRoZXIgd2UgY2FuDQpjcmVhdGUgdGhlIHN1YnNwYWNlIG9mIHRoZSBncmFwaCBleHBsb3JlZCBieSB0aGUgc2ltdWxhdGlvbiBzZXQuIA0KVGhpcyBpcyBwcm9iYWJseSBvZiB0aGUgbG93ZXN0IHByaW9yaXR5IG9mIHRoZSB0aGluZ3MgZGVzY3JpYmVkIGFib3ZlLCBidXQgdGhpcw0KaXMgb2YgbWF0aGVtYXRpY2FsIGludGVyZXN0IGFuZCBkb2VzIGhhdmUgc29tZSByZWxhdGlvbnNoaXAgdG8gZGl2ZXJzaXR5IA0KbWVhc3VyZXMuDQpTdWNoIGEgbWFwIHdvdWxkIHVzdWFsbHkgYmUgdmlzdWFsaXNlZCB3aXRoIGNvbHVtbnMgcmVwcmVzZW50aW5nIHJpY2huZXNzIGFuZA0KZWRnZXMgcmVwcmVzZW50aW5nIG1vdmVzIGFsb25nIHRoZSBzeXN0ZW0uDQpXZSBjYW4gY29sb3VyIHRoZSBlZGdlcyB0byByZXByZXNlbnQgd2hldGhlciB0aGV5IHdlcmUgdmlhIGFycml2YWwsIGR5bmFtaWMNCmV4dGluY3Rpb24sIG9yIG5ldXRyYWwgZXh0aW5jdGlvbiBhcyB3ZWxsLiAtLT4NCg0KIyMgUmVzdWx0cywgTGluZSBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCmBgYHtyfQ0KaWZybShEaXZlcnNpdHkpDQppZnJtKEVudkRpc3RDbHVzdCkNCmlmcm0oRW52RGlzdENsdXN0UEEpDQppZnJtKEVudkRpdmVyc2l0eSkNCmlmcm0oRW52aXJvbm1lbnRzKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlUEEpDQpgYGANCg0KV2UgcmVwZWF0IHRoZSBwcm9jZWR1cmVzIGFib3ZlIGZvciB0aGUgbGluZWFyIHNldC4NCldlIHVzZSB0aGUgc2FtZSBwb29sIGFuZCBpbnRlcmFjdGlvbiBtYXRyaWNlcywgYnV0IGNoYW5nZSB0aGUgc3BhY2Ugc28gdGhhdA0KYGBhZGphY2VudCcnIGNvbW11bml0aWVzIGNhbiBkaXNwZXJzZSAoZS5nLiAxIDwtPiAyIDwtPiAzIDwtPi4uLjwtPiA5IDwtPiAxMCkuDQpTaW1pbGFybHksIHRoZSBoaXN0b3J5IGlzIHRoZSBzYW1lIGluIHRlcm1zIG9mIGFycml2YWxzIGFuZCBleHRpbmN0aW9ucy4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQtUmVzdWx0LUVudjEwLUxpbmUuUkRhdGEiKQ0KKQ0KdG9SZW1vdmUgPC0gcmVzdWx0JEFidW5kYW5jZSA8PSByZXN1bHQkUGFyYW1ldGVycyRFbGltaW5hdGlvblRocmVzaG9sZA0KcmVzdWx0JEFidW5kYW5jZVt0b1JlbW92ZV0gPC0gMA0KaWZybSh0b1JlbW92ZSkNCmBgYA0KDQojIyMgQWJ1bmRhbmNlIHsudGFic2V0fQ0KDQpXaXRoIDEwIGVudmlyb25tZW50cywgaXQgaXMgcHJvYmFibHkgbm90IGhlbHBmdWwgdG8gY2hlY2sgMTAgaW5kaXZpZHVhbCBhYnVuZGFuY2UgY3VydmVzLCBidXQgbG9va2luZyBhdCB0aGUgZmlyc3Qgb25lIG1pZ2h0IGJlIGhlbHBmdWwuDQpgYGB7cn0NCkxhd01vcnRvbjE5OTZfUGxvdEFidW5kYW5jZShyZXN1bHQkQWJ1bmRhbmNlW3NlcShmcm9tID0gMSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG8gPSBucm93KHJlc3VsdCRBYnVuZGFuY2UpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IDEwKSwgYygxLCAyOjEwMSldKSAtPiBvYmo7DQpgYGANCmBgYHtyfQ0Kb2JqICsgZ2dwbG90Mjo6c2NhbGVfeV9sb2cxMCgpICsgZ2dwbG90Mjo6Z3VpZGVzKGNvbG9yID0gRkFMU0UpDQpgYGANCkV2ZXJ5IHZlcnRpY2FsIGxpbmUgaXMgYSBzcGVjaWVzIGludHJvZHVjdGlvbiBvciBleHRpbmN0aW9uIGJ5IG5ldXRyYWwgZHluYW1pY3MuDQpOb3RlIHRoYXQsIGR1ZSB0byBkaXNwZXJzYWwsIHRoZXNlIHNob3VsZCBsb3NlIHNvbWUgb2YgdGhlaXIgYWJ1bmRhbmNlIHRvIA0Kc3ByZWFkaW5nLCBidXQgaWYgdGhhdCBzcHJlYWRpbmcgaXMgZmFzdGVyIHRoYW4gdGhlIHBvcHVsYXRpb24gY2FuIHJlY291cCBpdHMNCmxvc3NlcywgdGhhbiBpdCBjYW4gaW52YWRlIGFuZCBiZSB3aXBlZCBvdXQgKHNpbmNlIGludmFkYWJpbGl0eSBkb2VzIG5vdCANCmNvbnNpZGVyIHNwYXRpYWwgZHluYW1pY3MsIG9ubHkgbG9jYWwgZWNvbG9naWNhbCBkeW5hbWljcykuDQpUaGUgcHJpbWFyeSBub3RlcyB0aGVuIGFyZSB0aGF0IHRoZXJlIGFyZSBhIGxhcmdlIG51bWJlciBtb3JlIGV2ZW50cyBhcHBlYXJpbmcNCnRvIG9jY3VyIChkdWUgdG8gdGhlIHNwYXRpYWwgYXJyYW5nZW1lbnQpLCBidXQgdGhlIHN5c3RlbSBhcyBhIHdob2xlIGlzIHN0YWJsZXINCihzaW5jZSB0aGUgc3BlY2llcyB0aGF0IGdldCBrbm9ja2VkIG91dCBjYW4gYmUgcmVjYXB0dXJlZCBmcm9tIHNwYWNlIGFzIHdlbGwpLg0KDQojIyMjIEFscGhhIERpdmVyc2l0eSBwbG90cyB7LnRhYnNldH0NCg0KIyMjIyMgSW50cm8NCmBgYHtyfQ0KRGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgIGFidW5kU3VtIDwtIHJvd1N1bXMoZW52KQ0KICAgIGVudHJvcHkgPC0gZW52IC8gYWJ1bmRTdW0NCiAgICBlbnRyb3B5IDwtIC0gYXBwbHkoDQogICAgICBlbnRyb3B5LCBNQVJHSU4gPSAxLA0KICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICBzdW0oaWZlbHNlKHggIT0gMCwgeCAqIGxvZyh4KSwgMCkpDQogICAgICB9KQ0KICAgIHNwZWNpZXMgPC0gYXBwbHkoDQogICAgICBlbnYsIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgIH0NCiAgICApDQogICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgIEVudHJvcHkgPSBlbnRyb3B5LA0KICAgICAgICAgICAgICAgU3BlY2llcyA9IHNwZWNpZXMsDQogICAgICAgICAgICAgICBFbnZpcm9ubWVudCA9IGkpDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSwNCiAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCg0KRGl2ZXJzaXR5IDwtIGRwbHlyOjpiaW5kX3Jvd3MoRGl2ZXJzaXR5KQ0KRGl2ZXJzaXR5IDwtIERpdmVyc2l0eSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgRXZlbm5lc3MgPSBFbnRyb3B5IC8gbG9nKFJpY2huZXNzKQ0KKQ0KYGBgDQoNCg0KIyMjIyMgUmljaG5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBNZWFuID0gbWVhbihSaWNobmVzcyksDQogICAgICBTcGVjaWVzVG90YWwgPSB0b1N0cmluZyhzb3J0KHVuaXF1ZSh1bmxpc3Qoc3Ryc3BsaXQocGFzdGUoDQogICAgICAgIFNwZWNpZXMsIGNvbGxhcHNlID0gIiwgIiksIHNwbGl0ID0gIiwgIiwgZml4ZWQgPSBUUlVFKSkpKSksDQogICAgICBHYW1tYSA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQoDQogICAgICAgIFNwZWNpZXNUb3RhbCwgc3BsaXQgPSAiLCAiLCBmaXhlZCA9IFRSVUUpLCBsZW5ndGgpKQ0KICAgICkgJT4lIHRpZHlyOjpwaXZvdF9sb25nZXIoDQogICAgICBjb2xzID0gYyhNZWFuLCBHYW1tYSksIA0KICAgICAgbmFtZXNfdG8gPSAiQWdncmVnYXRpb24iLA0KICAgICAgdmFsdWVzX3RvID0gIlJpY2huZXNzIg0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IFJpY2huZXNzLA0KICAgIGxpbmV0eXBlID0gQWdncmVnYXRpb24NCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCg0KIyMjIyMgRW50cm9weQ0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIERpdmVyc2l0eSwgDQogIGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRW50cm9weSwNCiAgICBjb2xvciA9IGZhY3RvcihFbnZpcm9ubWVudCksDQogICAgYWxwaGEgPSBpZmVsc2UoRW52aXJvbm1lbnQgPT0gMSwgMSwgMC4zKQ0KICApDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCiAgZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIFRpbWUNCiAgICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgICAgRW50cm9weSA9IG1lYW4oRW50cm9weSkNCiAgICApLA0KICBtYXBwaW5nID0gZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5DQogICksDQogIGNvbG9yID0gImJsYWNrIiwNCiAgaW5oZXJpdC5hZXMgPSBGQUxTRQ0KKSArIGdncGxvdDI6Omd1aWRlcygNCiAgYWxwaGEgPSAibm9uZSINCikgKyBnZ3Bsb3QyOjpzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgIkVudmlyb25tZW50Ig0KKQ0KYGBgDQoNCiMjIyMjIEVudHJvcHktUmljaG5lc3MNCmBgYHtyfQ0KcGxvdGx5OjpwbG90X2x5KGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpmaWx0ZXIoRW52aXJvbm1lbnQgPCAyKSwNCiAgICAgICAgICAgICAgICB4ID0gflJpY2huZXNzLCB5ID0gfkVudHJvcHksIHogPSB+VGltZSwgdHlwZSA9ICJzY2F0dGVyM2QiLA0KICAgICAgICAgICAgICAgIG1vZGUgPSAibGluZXMiLCBvcGFjaXR5ID0gMSwgbGluZSA9IGxpc3QoY29sb3IgPSB+VGltZSkpDQpgYGANCg0KIyMjIyMgRXZlbm5lc3MNCmBgYHtyfQ0KZ2dwbG90Mjo6Z2dwbG90KA0KICBEaXZlcnNpdHksIA0KICBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEV2ZW5uZXNzLA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFdmVubmVzcyA9IG1lYW4oRXZlbm5lc3MpDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCg0KIyMjIyMgRW52aXJvbm1lbnQgRGl2ZXJzaXR5DQoNCmBgYHtyfQ0KRW52RGl2ZXJzaXR5IDwtIGxhcHBseSgNCiAgICAxOigobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cyksDQogICAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICAgICAgdGltZSA8LSBhYnVuZFssIDFdDQogICAgICAgIGVudiA8LSBhYnVuZFssIDEgKyBpICsgbnVtU3BlY2llcyAqICgxOnJlc3VsdCROdW1FbnZpcm9ubWVudHMgLSAxKV0NCiAgICAgICAgcmljaG5lc3MgPC0gcm93U3VtcyhlbnYgIT0gMCkNCiAgICAgICAgYWJ1bmRTdW0gPC0gcm93U3VtcyhlbnYpDQogICAgICAgIGVudmlyb25tZW50cyA8LSBhcHBseSgNCiAgICAgICAgICAgIGVudiwgTUFSR0lOID0gMSwNCiAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgICAgICAgICB0b1N0cmluZyh3aGljaCh4ID4gMCkpDQogICAgICAgICAgICB9DQogICAgICAgICkNCiAgICAgICAgZGF0YS5mcmFtZShUaW1lID0gdGltZSwgDQogICAgICAgICAgICAgICAgICAgUmljaG5lc3MgPSByaWNobmVzcywgDQogICAgICAgICAgICAgICAgICAgQWJ1bmRhbmNlID0gYWJ1bmRTdW0sDQogICAgICAgICAgICAgICAgICAgU3BlY2llcyA9IGksDQogICAgICAgICAgICAgICAgICAgRW52aXJvbm1lbnRzID0gZW52aXJvbm1lbnRzKQ0KICAgIH0sDQogICAgYWJ1bmQgPSByZXN1bHQkQWJ1bmRhbmNlLA0KICAgIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZEaXZlcnNpdHkgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZEaXZlcnNpdHkpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIEVudkRpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihSaWNobmVzcyA+IDEpLCANCiAgYWVzKHggPSBUaW1lLCB5ID0gUmljaG5lc3MsIGNvbG9yID0gU3BlY2llcykNCikgKyBnZW9tX3BvaW50KA0KICBhbHBoYSA9IDAuMDEsIHNpemUgPSAzDQopICsgZ3VpZGVzKA0KICBjb2xvciA9ICJub25lIg0KKQ0KYGBgDQoNCmBgYHtyfQ0Kd2l0aChFbnZEaXZlcnNpdHkgJT4lIG11dGF0ZSgNCiAgVGltZSA9IGZsb29yKFRpbWUpDQogICkgJT4lIGdyb3VwX2J5KA0KICAgIFRpbWUsIFNwZWNpZXMNCiAgICApICU+JSBzdW1tYXJpc2UoDQogICAgICBSaWNobmVzcyA9IHJvdW5kKG1lYW4oUmljaG5lc3MpKSwNCiAgICAgIC5ncm91cHMgPSAiZHJvcCINCiAgICAgICksDQogICAgIHRhYmxlKFNwZWNpZXMsIFJpY2huZXNzKSkNCmBgYA0KIyMjIyBCZXRhIERpdmVyc2l0eSB7LnRhYnNldH0NCg0KIyMjIyMgUHJpbmNpcGFsIENvbXBvbmVudHMgQW5hbHlzaXMNCmBgYHtyfQ0KQXZlcmFnZWRBYnVuZGFuY2UgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KYGBge3J9DQpQQ0EgPC0gcHJjb21wKEF2ZXJhZ2VkQWJ1bmRhbmNlICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6YXV0b3Bsb3QoUENBLCBsb2FkaW5ncyA9IFRSVUUsIA0KICAgICAgICAgICAgICAgICAgZGF0YSA9IEF2ZXJhZ2VkQWJ1bmRhbmNlICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSAidGltZSIpDQpgYGANCg0KYGBge3J9DQpBdmVyYWdlZFBBIDwtIHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgZHBseXI6OmFjcm9zcyguY29scyA9ICF0aW1lLCAuZm5zID0gfiAueCA+IDApDQopICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogIHRpbWUNCikgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQopDQpgYGANCg0KYGBge3J9DQpQQ0FQQSA8LSBwcmNvbXAoQXZlcmFnZWRQQSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLCANCiAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwgc2NhbGUuID0gVFJVRSwgcmFuay4gPSAyNSkNCmBgYA0KDQpgYGB7cn0NCmhlYWQoc3VtbWFyeShQQ0FQQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90Mjo6YXV0b3Bsb3QoUENBUEEsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRQQSAlPiUgZHBseXI6OnNlbGVjdF9pZih+IGFueSguID4gMCkpLA0KICAgICAgICAgICAgICAgICAgY29sb3VyID0gInRpbWUiKQ0KYGBgDQoNCmBgYHtyfSANCnN1bShzdW1tYXJ5KFBDQSkkaW1wb3J0YW5jZVsyLCBdKQ0KYGBgIA0KDQpgYGB7ciBjbGVhbnVwMn0NCmlmcm0oQXZlcmFnZWRBYnVuZGFuY2UpDQppZnJtKEF2ZXJhZ2VkUEEpDQppZnJtKFBDQSkNCmlmcm0oUENBUEEpDQpgYGANCg0KIyMjIyMgQmV0YSBEaXZlcnNpdHkgT3ZlciBUaW1lDQpgYGB7cn0NCkVudmlyb25tZW50cyA8LSBsYXBwbHkoDQogIDE6cmVzdWx0JE51bUVudmlyb25tZW50cywNCiAgZnVuY3Rpb24oaSwgYWJ1bmQsIG51bVNwZWNpZXMpIHsNCiAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICBlbnYgPC0gYWJ1bmRbLCAxICsgMTpudW1TcGVjaWVzICsgbnVtU3BlY2llcyAqIChpIC0gMSldDQogICAgDQogICAgcmV0dmFsIDwtIGRhdGEuZnJhbWUoRW52aXJvbm1lbnQgPSB0b1N0cmluZyhpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lID0gdGltZQ0KICAgICkNCiAgICANCiAgICByZXR2YWwgPC0gY2JpbmQocmV0dmFsLCBlbnYpDQogICAgY29sbmFtZXMocmV0dmFsKSA8LSBjKCJFbnZpcm9ubWVudCIsICJUaW1lIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQmFzYWwiLCAxOjM0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiQ29uc3VtZXIiLCAzNToxMDApDQogICAgKQ0KICAgIHJldHVybihyZXR2YWwpDQogICAgICAgICAgICAgICAgICAgICAgICAgIA0KICB9LA0KICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UgJT4lIGRhdGEuZnJhbWUoDQogICkgJT4lIGRwbHlyOjptdXRhdGUoDQogICAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCiAgKSAlPiUgZHBseXI6Omdyb3VwX2J5KA0KICAgIHRpbWUNCiAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICBkcGx5cjo6YWNyb3NzKC5mbnMgPSB+IG1lYW4oLngpKQ0KICApLA0KICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KRW52aXJvbm1lbnRzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoRW52aXJvbm1lbnRzKQ0KRW52aXJvbm1lbnRzIDwtIEVudmlyb25tZW50cyAlPiUgZHBseXI6OmZpbHRlcigNCiAgZHBseXI6OmlmX2FueShkcGx5cjo6c3RhcnRzX3dpdGgoYygiQmFzYWwiLCAiQ29uc3VtZXIiKSksIH4gKC54ICE9IDApKSwNCikNCmBgYA0KDQojIyMjIyBDbHVzdGVyIEFuYWx5c2lzIE92ZXIgVGltZQ0KDQpgYGB7cn0NCkVudmlyb25tZW50RGlzdGFuY2UgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3QgPC0gaGNsdXN0KEVudmlyb25tZW50RGlzdGFuY2UpDQpgYGANCg0KYGBge3J9DQpwbG90KEVudkRpc3RDbHVzdCwgbGFiZWxzID0gRkFMU0UpDQpgYGANCg0KYGBge3J9DQpFbnZpcm9ubWVudERpc3RhbmNlUEEgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6c2VsZWN0KA0KICAtRW52aXJvbm1lbnQsIC1UaW1lDQopICU+JSB2ZWdhbjo6dmVnZGlzdChtZXRob2QgPSAiamFjY2FyZCIsIGJpbmFyeSA9IFRSVUUpDQpgYGANCg0KYGBge3J9DQpFbnZEaXN0Q2x1c3RQQSA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZVBBKQ0KYGBgDQoNCmBgYHtyfQ0KcGxvdChFbnZEaXN0Q2x1c3RQQSwgbGFiZWxzID0gRkFMU0UpDQpgYGANCiMjIFJlc3VsdHMsIFNsb3cgTGluZSBTcGF0aWFsIFN0cnVjdHVyZSB7LnRhYnNldH0NCmBgYHtyfQ0KaWZybShEaXZlcnNpdHkpDQppZnJtKEVudkRpc3RDbHVzdCkNCmlmcm0oRW52RGlzdENsdXN0UEEpDQppZnJtKEVudkRpdmVyc2l0eSkNCmlmcm0oRW52aXJvbm1lbnRzKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlKQ0KaWZybShFbnZpcm9ubWVudERpc3RhbmNlUEEpDQpgYGANCg0KV2UgcmVwZWF0IHRoZSBwcm9jZWR1cmVzIGFib3ZlIGZvciB0aGUgbGluZWFyIHNldC4NCldlIHVzZSB0aGUgc2FtZSBwb29sIGFuZCBpbnRlcmFjdGlvbiBtYXRyaWNlcywgYnV0IGNoYW5nZSB0aGUgc3BhY2Ugc28gdGhhdA0KYGBhZGphY2VudCcnIGNvbW11bml0aWVzIGNhbiBkaXNwZXJzZSAoZS5nLiAxIDwtPiAyIDwtPiAzIDwtPi4uLjwtPiA5IDwtPiAxMCkuDQpTaW1pbGFybHksIHRoZSBoaXN0b3J5IGlzIHRoZSBzYW1lIGluIHRlcm1zIG9mIGFycml2YWxzIGFuZCBleHRpbmN0aW9ucy4NCg0KYGBge3J9DQpsb2FkKGZpbGUucGF0aCgNCiAgIi4uIiwgImV4cGVyaW1lbnRzIiwgIk1OQS1GaXJzdEF0dGVtcHQxZSswNi1SZXN1bHQtRW52MTAtTGluZS5SRGF0YSIpDQopDQp0b1JlbW92ZSA8LSByZXN1bHQkQWJ1bmRhbmNlIDw9IHJlc3VsdCRQYXJhbWV0ZXJzJEVsaW1pbmF0aW9uVGhyZXNob2xkDQpyZXN1bHQkQWJ1bmRhbmNlWzE6KGxlbmd0aCh0b1JlbW92ZSkvMildW3RvUmVtb3ZlWzE6KGxlbmd0aCh0b1JlbW92ZSkvMildXSA8LSAwDQpyZXN1bHQkQWJ1bmRhbmNlWyhsZW5ndGgodG9SZW1vdmUpLzIgKyAxKTpsZW5ndGgodG9SZW1vdmUpXVt0b1JlbW92ZVsobGVuZ3RoKHRvUmVtb3ZlKS8yICsgMSk6bGVuZ3RoKHRvUmVtb3ZlKV1dIDwtIDANCmlmcm0odG9SZW1vdmUpDQpgYGANCg0KIyMjIEFidW5kYW5jZSB7LnRhYnNldH0NCg0KV2l0aCAxMCBlbnZpcm9ubWVudHMsIGl0IGlzIHByb2JhYmx5IG5vdCBoZWxwZnVsIHRvIGNoZWNrIDEwIGluZGl2aWR1YWwgYWJ1bmRhbmNlIGN1cnZlcywgYnV0IGxvb2tpbmcgYXQgdGhlIGZpcnN0IG9uZSBtaWdodCBiZSBoZWxwZnVsLg0KYGBge3J9DQpMYXdNb3J0b24xOTk2X1Bsb3RBYnVuZGFuY2UocmVzdWx0JEFidW5kYW5jZVtzZXEoZnJvbSA9IDEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvID0gbnJvdyhyZXN1bHQkQWJ1bmRhbmNlKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSAxMCksIGMoMSwgMjoxMDEpXSkgLT4gb2JqOw0KYGBgDQpgYGB7cn0NCm9iaiArIGdncGxvdDI6OnNjYWxlX3lfbG9nMTAoKSArIGdncGxvdDI6Omd1aWRlcyhjb2xvciA9IEZBTFNFKQ0KYGBgDQoNCiMjIyMgQWxwaGEgRGl2ZXJzaXR5IHBsb3RzIHsudGFic2V0fQ0KDQojIyMjIyBJbnRybw0KYGBge3J9DQpEaXZlcnNpdHkgPC0gbGFwcGx5KA0KICAxOnJlc3VsdCROdW1FbnZpcm9ubWVudHMsDQogIGZ1bmN0aW9uKGksIGFidW5kLCBudW1TcGVjaWVzKSB7DQogICAgdGltZSA8LSBhYnVuZFssIDFdDQogICAgZW52IDwtIGFidW5kWywgMSArIDE6bnVtU3BlY2llcyArIG51bVNwZWNpZXMgKiAoaSAtIDEpXQ0KICAgIHJpY2huZXNzIDwtIHJvd1N1bXMoZW52ICE9IDApDQogICAgYWJ1bmRTdW0gPC0gcm93U3VtcyhlbnYpDQogICAgZW50cm9weSA8LSBlbnYgLyBhYnVuZFN1bQ0KICAgIGVudHJvcHkgPC0gLSBhcHBseSgNCiAgICAgIGVudHJvcHksIE1BUkdJTiA9IDEsDQogICAgICBGVU4gPSBmdW5jdGlvbih4KSB7DQogICAgICAgIHN1bShpZmVsc2UoeCAhPSAwLCB4ICogbG9nKHgpLCAwKSkNCiAgICAgIH0pDQogICAgc3BlY2llcyA8LSBhcHBseSgNCiAgICAgIGVudiwgTUFSR0lOID0gMSwNCiAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIHsNCiAgICAgICAgdG9TdHJpbmcod2hpY2goeCA+IDApKQ0KICAgICAgfQ0KICAgICkNCiAgICBkYXRhLmZyYW1lKFRpbWUgPSB0aW1lLCANCiAgICAgICAgICAgICAgIFJpY2huZXNzID0gcmljaG5lc3MsIA0KICAgICAgICAgICAgICAgRW50cm9weSA9IGVudHJvcHksDQogICAgICAgICAgICAgICBTcGVjaWVzID0gc3BlY2llcywNCiAgICAgICAgICAgICAgIEVudmlyb25tZW50ID0gaSkNCiAgfSwNCiAgYWJ1bmQgPSByZXN1bHQkQWJ1bmRhbmNlLA0KICBudW1TcGVjaWVzID0gKG5jb2wocmVzdWx0JEFidW5kYW5jZSkgLSAxKSAvIHJlc3VsdCROdW1FbnZpcm9ubWVudHMNCikNCg0KDQpEaXZlcnNpdHkgPC0gZHBseXI6OmJpbmRfcm93cyhEaXZlcnNpdHkpDQpEaXZlcnNpdHkgPC0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6bXV0YXRlKA0KICBFdmVubmVzcyA9IEVudHJvcHkgLyBsb2coUmljaG5lc3MpDQopDQpgYGANCg0KDQojIyMjIyBSaWNobmVzcw0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIERpdmVyc2l0eSwgDQogIGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gUmljaG5lc3MsDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIE1lYW4gPSBtZWFuKFJpY2huZXNzKSwNCiAgICAgIFNwZWNpZXNUb3RhbCA9IHRvU3RyaW5nKHNvcnQodW5pcXVlKHVubGlzdChzdHJzcGxpdChwYXN0ZSgNCiAgICAgICAgU3BlY2llcywgY29sbGFwc2UgPSAiLCAiKSwgc3BsaXQgPSAiLCAiLCBmaXhlZCA9IFRSVUUpKSkpKSwNCiAgICAgIEdhbW1hID0gdW5saXN0KGxhcHBseShzdHJzcGxpdCgNCiAgICAgICAgU3BlY2llc1RvdGFsLCBzcGxpdCA9ICIsICIsIGZpeGVkID0gVFJVRSksIGxlbmd0aCkpDQogICAgKSAlPiUgdGlkeXI6OnBpdm90X2xvbmdlcigNCiAgICAgIGNvbHMgPSBjKE1lYW4sIEdhbW1hKSwgDQogICAgICBuYW1lc190byA9ICJBZ2dyZWdhdGlvbiIsDQogICAgICB2YWx1ZXNfdG8gPSAiUmljaG5lc3MiDQogICAgKSwNCiAgbWFwcGluZyA9IGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gUmljaG5lc3MsDQogICAgbGluZXR5cGUgPSBBZ2dyZWdhdGlvbg0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KDQojIyMjIyBFbnRyb3B5DQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRGl2ZXJzaXR5LCANCiAgZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFbnRyb3B5LA0KICAgIGNvbG9yID0gZmFjdG9yKEVudmlyb25tZW50KSwNCiAgICBhbHBoYSA9IGlmZWxzZShFbnZpcm9ubWVudCA9PSAxLCAxLCAwLjMpDQogICkNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQopICsgZ2dwbG90Mjo6Z2VvbV9saW5lKA0KICBkYXRhID0gRGl2ZXJzaXR5ICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgVGltZQ0KICAgICkgJT4lIGRwbHlyOjpzdW1tYXJpc2UoDQogICAgICBFbnRyb3B5ID0gbWVhbihFbnRyb3B5KQ0KICAgICksDQogIG1hcHBpbmcgPSBnZ3Bsb3QyOjphZXMoDQogICAgeCA9IFRpbWUsDQogICAgeSA9IEVudHJvcHkNCiAgKSwNCiAgY29sb3IgPSAiYmxhY2siLA0KICBpbmhlcml0LmFlcyA9IEZBTFNFDQopICsgZ2dwbG90Mjo6Z3VpZGVzKA0KICBhbHBoYSA9ICJub25lIg0KKSArIGdncGxvdDI6OnNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAiRW52aXJvbm1lbnQiDQopDQpgYGANCg0KIyMjIyMgRW50cm9weS1SaWNobmVzcw0KYGBge3J9DQpwbG90bHk6OnBsb3RfbHkoZGF0YSA9IERpdmVyc2l0eSAlPiUgZHBseXI6OmZpbHRlcihFbnZpcm9ubWVudCA8IDIpLA0KICAgICAgICAgICAgICAgIHggPSB+UmljaG5lc3MsIHkgPSB+RW50cm9weSwgeiA9IH5UaW1lLCB0eXBlID0gInNjYXR0ZXIzZCIsDQogICAgICAgICAgICAgICAgbW9kZSA9ICJsaW5lcyIsIG9wYWNpdHkgPSAxLCBsaW5lID0gbGlzdChjb2xvciA9IH5UaW1lKSkNCmBgYA0KDQojIyMjIyBFdmVubmVzcw0KYGBge3J9DQpnZ3Bsb3QyOjpnZ3Bsb3QoDQogIERpdmVyc2l0eSwgDQogIGdncGxvdDI6OmFlcygNCiAgICB4ID0gVGltZSwNCiAgICB5ID0gRXZlbm5lc3MsDQogICAgY29sb3IgPSBmYWN0b3IoRW52aXJvbm1lbnQpLA0KICAgIGFscGhhID0gaWZlbHNlKEVudmlyb25tZW50ID09IDEsIDEsIDAuMykNCiAgKQ0KKSArIGdncGxvdDI6Omdlb21fbGluZSgNCikgKyBnZ3Bsb3QyOjpnZW9tX2xpbmUoDQogIGRhdGEgPSBEaXZlcnNpdHkgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgICBUaW1lDQogICAgKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgICAgIEV2ZW5uZXNzID0gbWVhbihFdmVubmVzcykNCiAgICApLA0KICBtYXBwaW5nID0gZ2dwbG90Mjo6YWVzKA0KICAgIHggPSBUaW1lLA0KICAgIHkgPSBFdmVubmVzcw0KICApLA0KICBjb2xvciA9ICJibGFjayIsDQogIGluaGVyaXQuYWVzID0gRkFMU0UNCikgKyBnZ3Bsb3QyOjpndWlkZXMoDQogIGFscGhhID0gIm5vbmUiDQopICsgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZGlzY3JldGUoDQogICJFbnZpcm9ubWVudCINCikNCmBgYA0KDQojIyMjIyBFbnZpcm9ubWVudCBEaXZlcnNpdHkNCg0KYGBge3J9DQpFbnZEaXZlcnNpdHkgPC0gbGFwcGx5KA0KICAgIDE6KChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzKSwNCiAgICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgICAgICB0aW1lIDwtIGFidW5kWywgMV0NCiAgICAgICAgZW52IDwtIGFidW5kWywgMSArIGkgKyBudW1TcGVjaWVzICogKDE6cmVzdWx0JE51bUVudmlyb25tZW50cyAtIDEpXQ0KICAgICAgICByaWNobmVzcyA8LSByb3dTdW1zKGVudiAhPSAwKQ0KICAgICAgICBhYnVuZFN1bSA8LSByb3dTdW1zKGVudikNCiAgICAgICAgZW52aXJvbm1lbnRzIDwtIGFwcGx5KA0KICAgICAgICAgICAgZW52LCBNQVJHSU4gPSAxLA0KICAgICAgICAgICAgRlVOID0gZnVuY3Rpb24oeCkgew0KICAgICAgICAgICAgICAgIHRvU3RyaW5nKHdoaWNoKHggPiAwKSkNCiAgICAgICAgICAgIH0NCiAgICAgICAgKQ0KICAgICAgICBkYXRhLmZyYW1lKFRpbWUgPSB0aW1lLCANCiAgICAgICAgICAgICAgICAgICBSaWNobmVzcyA9IHJpY2huZXNzLCANCiAgICAgICAgICAgICAgICAgICBBYnVuZGFuY2UgPSBhYnVuZFN1bSwNCiAgICAgICAgICAgICAgICAgICBTcGVjaWVzID0gaSwNCiAgICAgICAgICAgICAgICAgICBFbnZpcm9ubWVudHMgPSBlbnZpcm9ubWVudHMpDQogICAgfSwNCiAgICBhYnVuZCA9IHJlc3VsdCRBYnVuZGFuY2UsDQogICAgbnVtU3BlY2llcyA9IChuY29sKHJlc3VsdCRBYnVuZGFuY2UpIC0gMSkgLyByZXN1bHQkTnVtRW52aXJvbm1lbnRzDQopDQoNCkVudkRpdmVyc2l0eSA8LSBkcGx5cjo6YmluZF9yb3dzKEVudkRpdmVyc2l0eSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdDI6OmdncGxvdCgNCiAgRW52RGl2ZXJzaXR5ICU+JSBkcGx5cjo6ZmlsdGVyKFJpY2huZXNzID4gMSksIA0KICBhZXMoeCA9IFRpbWUsIHkgPSBSaWNobmVzcywgY29sb3IgPSBTcGVjaWVzKQ0KKSArIGdlb21fcG9pbnQoDQogIGFscGhhID0gMC4wMSwgc2l6ZSA9IDMNCikgKyBndWlkZXMoDQogIGNvbG9yID0gIm5vbmUiDQopDQpgYGANCg0KYGBge3J9DQp3aXRoKEVudkRpdmVyc2l0eSAlPiUgbXV0YXRlKA0KICBUaW1lID0gZmxvb3IoVGltZSkNCiAgKSAlPiUgZ3JvdXBfYnkoDQogICAgVGltZSwgU3BlY2llcw0KICAgICkgJT4lIHN1bW1hcmlzZSgNCiAgICAgIFJpY2huZXNzID0gcm91bmQobWVhbihSaWNobmVzcykpLA0KICAgICAgLmdyb3VwcyA9ICJkcm9wIg0KICAgICAgKSwNCiAgICAgdGFibGUoU3BlY2llcywgUmljaG5lc3MpKQ0KYGBgDQojIyMjIEJldGEgRGl2ZXJzaXR5IHsudGFic2V0fQ0KDQojIyMjIyBQcmluY2lwYWwgQ29tcG9uZW50cyBBbmFseXNpcw0KYGBge3J9DQpBdmVyYWdlZEFidW5kYW5jZSA8LSByZXN1bHQkQWJ1bmRhbmNlICU+JSBkYXRhLmZyYW1lKA0KKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgdGltZSA9IGZsb29yKHRpbWUvMTAwKSoxMDANCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgdGltZQ0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCikNCmBgYA0KDQpgYGB7cn0NClBDQSA8LSBwcmNvbXAoQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwgDQogICAgICAgICAgICAgIGNlbnRlciA9IFRSVUUsIHNjYWxlLiA9IFRSVUUsIHJhbmsuID0gMjUpDQpgYGANCg0KYGBge3J9DQpoZWFkKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KYGBge3J9IA0Kc3VtKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0EsIGxvYWRpbmdzID0gVFJVRSwgDQogICAgICAgICAgICAgICAgICBkYXRhID0gQXZlcmFnZWRBYnVuZGFuY2UgJT4lIGRwbHlyOjpzZWxlY3RfaWYofiBhbnkoLiA+IDApKSwNCiAgICAgICAgICAgICAgICAgIGNvbG91ciA9ICJ0aW1lIikNCmBgYA0KDQpgYGB7cn0NCkF2ZXJhZ2VkUEEgPC0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCikgJT4lIGRwbHlyOjptdXRhdGUoDQogIHRpbWUgPSBmbG9vcih0aW1lLzEwMCkqMTAwDQopICU+JSBkcGx5cjo6bXV0YXRlKA0KICBkcGx5cjo6YWNyb3NzKC5jb2xzID0gIXRpbWUsIC5mbnMgPSB+IC54ID4gMCkNCikgJT4lIGRwbHlyOjpncm91cF9ieSgNCiAgdGltZQ0KKSAlPiUgZHBseXI6OnN1bW1hcmlzZSgNCiAgZHBseXI6OmFjcm9zcyguZm5zID0gfiBtZWFuKC54KSkNCikNCmBgYA0KDQpgYGB7cn0NClBDQVBBIDwtIHByY29tcChBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksIA0KICAgICAgICAgICAgICBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFLCByYW5rLiA9IDI1KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhZChzdW1tYXJ5KFBDQVBBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QyOjphdXRvcGxvdChQQ0FQQSwgbG9hZGluZ3MgPSBUUlVFLCANCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBBdmVyYWdlZFBBICU+JSBkcGx5cjo6c2VsZWN0X2lmKH4gYW55KC4gPiAwKSksDQogICAgICAgICAgICAgICAgICBjb2xvdXIgPSAidGltZSIpDQpgYGANCg0KYGBge3J9IA0Kc3VtKHN1bW1hcnkoUENBKSRpbXBvcnRhbmNlWzIsIF0pDQpgYGAgDQoNCmBgYHtyIGNsZWFudXAzfQ0KaWZybShBdmVyYWdlZEFidW5kYW5jZSkNCmlmcm0oQXZlcmFnZWRQQSkNCmlmcm0oUENBKQ0KaWZybShQQ0FQQSkNCmBgYA0KDQojIyMjIyBCZXRhIERpdmVyc2l0eSBPdmVyIFRpbWUNCmBgYHtyfQ0KRW52aXJvbm1lbnRzIDwtIGxhcHBseSgNCiAgMTpyZXN1bHQkTnVtRW52aXJvbm1lbnRzLA0KICBmdW5jdGlvbihpLCBhYnVuZCwgbnVtU3BlY2llcykgew0KICAgIHRpbWUgPC0gYWJ1bmRbLCAxXQ0KICAgIGVudiA8LSBhYnVuZFssIDEgKyAxOm51bVNwZWNpZXMgKyBudW1TcGVjaWVzICogKGkgLSAxKV0NCiAgICANCiAgICByZXR2YWwgPC0gZGF0YS5mcmFtZShFbnZpcm9ubWVudCA9IHRvU3RyaW5nKGkpLA0KICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWUgPSB0aW1lDQogICAgKQ0KICAgIA0KICAgIHJldHZhbCA8LSBjYmluZChyZXR2YWwsIGVudikNCiAgICBjb2xuYW1lcyhyZXR2YWwpIDwtIGMoIkVudmlyb25tZW50IiwgIlRpbWUiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJCYXNhbCIsIDE6MzQpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGFzdGUwKCJDb25zdW1lciIsIDM1OjEwMCkNCiAgICApDQogICAgcmV0dXJuKHJldHZhbCkNCiAgICAgICAgICAgICAgICAgICAgICAgICAgDQogIH0sDQogIGFidW5kID0gcmVzdWx0JEFidW5kYW5jZSAlPiUgZGF0YS5mcmFtZSgNCiAgKSAlPiUgZHBseXI6Om11dGF0ZSgNCiAgICB0aW1lID0gZmxvb3IodGltZS8xMDApKjEwMA0KICApICU+JSBkcGx5cjo6Z3JvdXBfYnkoDQogICAgdGltZQ0KICApICU+JSBkcGx5cjo6c3VtbWFyaXNlKA0KICAgIGRwbHlyOjphY3Jvc3MoLmZucyA9IH4gbWVhbigueCkpDQogICksDQogIG51bVNwZWNpZXMgPSAobmNvbChyZXN1bHQkQWJ1bmRhbmNlKSAtIDEpIC8gcmVzdWx0JE51bUVudmlyb25tZW50cw0KKQ0KDQpFbnZpcm9ubWVudHMgPC0gZHBseXI6OmJpbmRfcm93cyhFbnZpcm9ubWVudHMpDQpFbnZpcm9ubWVudHMgPC0gRW52aXJvbm1lbnRzICU+JSBkcGx5cjo6ZmlsdGVyKA0KICBkcGx5cjo6aWZfYW55KGRwbHlyOjpzdGFydHNfd2l0aChjKCJCYXNhbCIsICJDb25zdW1lciIpKSwgfiAoLnggIT0gMCkpLA0KKQ0KYGBgDQoNCiMjIyMjIENsdXN0ZXIgQW5hbHlzaXMgT3ZlciBUaW1lDQoNCmBgYHtyfQ0KRW52aXJvbm1lbnREaXN0YW5jZSA8LSBFbnZpcm9ubWVudHMgJT4lIGRwbHlyOjpzZWxlY3QoDQogIC1FbnZpcm9ubWVudCwgLVRpbWUNCikgJT4lIHZlZ2FuOjp2ZWdkaXN0KG1ldGhvZCA9ICJqYWNjYXJkIikNCmBgYA0KDQpgYGB7cn0NCkVudkRpc3RDbHVzdCA8LSBoY2x1c3QoRW52aXJvbm1lbnREaXN0YW5jZSkNCmBgYA0KDQpgYGB7cn0NCnBsb3QoRW52RGlzdENsdXN0LCBsYWJlbHMgPSBGQUxTRSkNCmBgYA0KDQpgYGB7cn0NCkVudmlyb25tZW50RGlzdGFuY2VQQSA8LSBFbnZpcm9ubWVudHMgJT4lIGRwbHlyOjpzZWxlY3QoDQogIC1FbnZpcm9ubWVudCwgLVRpbWUNCikgJT4lIHZlZ2FuOjp2ZWdkaXN0KG1ldGhvZCA9ICJqYWNjYXJkIiwgYmluYXJ5ID0gVFJVRSkNCmBgYA0KDQpgYGB7cn0NCkVudkRpc3RDbHVzdFBBIDwtIGhjbHVzdChFbnZpcm9ubWVudERpc3RhbmNlUEEpDQpgYGANCg0KYGBge3J9DQpwbG90KEVudkRpc3RDbHVzdFBBLCBsYWJlbHMgPSBGQUxTRSkNCmBgYA0K